Skip to content

Deterministic JSON serialization and deserialization support in Painless (should be deterministic) #37585

@ypid-geberit

Description

@ypid-geberit

Describe the feature:

The Painless API reference does not contain any function that deals with JSON. Would it be possible to provide basic JSON functionally in Painless? If possible the serialization should be deterministic, see below.

I am including full ES document dumps in emails send by watches. Because the toJson Mustache function is not deterministic I needed to implement my own deterministic serialization function in Painless and realized that this is somewhat painful (e.g. not as it should be ;-) ).

This has been asked before: https://discuss.elastic.co/t/painless-scripting-json-functions/98511

For reference, this is my workaround. My code does not produce valid JSON and probably has bugs (not extensively tested). To work without regex support in Painless, I also put HTML indent support into this function.

/* JSON-like serialization Painless {{{
 * Ref: https://github.com/elastic/elasticsearch/issues/37585
 * License: Public domain (because the code is too simple).
 */
String get_indent_string(boolean html_output, int indent) {
  if (html_output) {
    return String.join("", Collections.nCopies(indent, "  "));
  } else {
    return String.join("", Collections.nCopies(indent, "  "));
  }
}
String serializ_leaf(def object) {
  if (object == null) {
    return "null";
  } else if (object instanceof String) {
    return '"' + object + '"';
  } else {
    return object.toString();
  }
}

ArrayList object_to_human_readable_string_lines(def object) {
  return object_to_human_readable_string_lines(object, false, 0, true);
}

ArrayList object_to_human_readable_string_lines(def object, boolean html_output) {
  return object_to_human_readable_string_lines(object, html_output, 0, true);
}

ArrayList object_to_human_readable_string_lines(def object, boolean html_output, int indent) {
  return object_to_human_readable_string_lines(object, html_output, indent, true);
}

ArrayList object_to_human_readable_string_lines(def object, boolean html_output, int indent, boolean indent_first_line) {
  ArrayList serialized_lines = new ArrayList();

  if (object instanceof Collection) {
    serialized_lines.add("[");
    indent += 1;
    for (def nested_object : object) {
      if (nested_object instanceof Collection || nested_object instanceof Map) {
        serialized_lines.addAll(object_to_human_readable_string_lines(nested_object, html_output, indent));
      } else {
        serialized_lines.add(get_indent_string(html_output, indent) + serializ_leaf(nested_object) + ",");
      }
    }
    indent -= 1;
    serialized_lines.add(get_indent_string(html_output, indent) + "]" + (indent == 0 ? "" : ","));
  } else if (object instanceof Map) {
    serialized_lines.add(get_indent_string(html_output, (indent_first_line ? indent : 0)) + "{");
    indent += 1;
    List keys = object.keySet().stream().sorted().collect(Collectors.toList());
    for (String key : keys) {
      def nested_object = object[key];
      if (nested_object instanceof Collection || nested_object instanceof Map) {
        ArrayList nested_serialized_lines = object_to_human_readable_string_lines(nested_object, html_output, indent, false);
        serialized_lines.add(get_indent_string(html_output, indent) + key + ": " + nested_serialized_lines.remove(0));
        serialized_lines.addAll(nested_serialized_lines);
      } else {
        serialized_lines.add(get_indent_string(html_output, indent) + key + ": " + serializ_leaf(nested_object) + ",");
      }
    }
    indent -= 1;
    serialized_lines.add(get_indent_string(html_output, indent) + "}" + (indent == 0 ? "" : ","));
  } else {
    serialized_lines.add(serializ_leaf(object) + ",");
  }

  return serialized_lines;
}
/* }}} */

Metadata

Metadata

Assignees

No one assigned

    Labels

    :Core/Infra/ScriptingScripting abstractions, Painless, and MustacheTeam:Core/InfraMeta label for core/infra team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions