All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.byu.hbll.box.internal.util.JsonUtils Maven / Gradle / Ivy

package edu.byu.hbll.box.internal.util;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import edu.byu.hbll.json.JsonField;
import edu.byu.hbll.json.ObjectMapperFactory;
import edu.byu.hbll.json.UncheckedObjectMapper;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Collection;
import java.util.Map;

/** Json utilities. */
public class JsonUtils {

  private static final UncheckedObjectMapper mapper = ObjectMapperFactory.newUnchecked();

  /**
   * Binds values in the map to the given {@link JsonNode}. The key in the map is the name of the
   * variable in the node. The value in the map replaces the ${VARIABLE_NAME} in the node.
   *
   * @param node the node to modify
   * @param values the values to bind to the node
   */
  public static void bind(JsonNode node, Map values) {

    int i = 0;

    for (JsonField field : new JsonField(node)) {
      JsonNode value = field.getValue();

      if (value.isTextual()) {
        String textValue = value.asText(null);

        if (textValue != null && textValue.startsWith("${") && textValue.endsWith("}")) {
          String key = textValue.substring(2, textValue.length() - 1);
          JsonNode finalValue = values.getOrDefault(key, value);

          if (node.isArray()) {
            ((ArrayNode) node).remove(i);
            ((ArrayNode) node).insert(i, finalValue);
          } else if (node.isObject()) {
            ((ObjectNode) node).set(field.getKey(), finalValue);
          }
        }
      } else if (value.isContainerNode()) {
        bind(value, values);
      }

      i++;
    }
  }

  /**
   * Binds the given {@link JsonNode} to the provided object.
   *
   * @param node the values to bind
   * @param object values are bound to this object
   */
  public static void bind(JsonNode node, Object object) {
    try {
      mapper.readerForUpdating(object).readValue(node);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  /**
   * Deserializes the string to a {@link JsonNode}.
   *
   * @param value the string to deserialize
   * @return the deserialized node
   */
  public static JsonNode deserialize(String value) {
    return mapper.readTree(value);
  }

  /**
   * Return the found {@link JsonNode} after following the dot notation path.
   *
   * @param node the subect
   * @param dotPath the path
   * @return the found node or missing node if none found
   */
  public static JsonNode dotPath(JsonNode node, String dotPath) {
    String[] path = dotPath.split("\\.");
    JsonNode currentNode = node;

    for (String fieldName : path) {
      currentNode = currentNode.path(fieldName);
    }

    return currentNode;
  }

  /**
   * Serializes the document to a string.
   *
   * @param document the document to serialize.
   * @return the serialized string
   */
  public static String serialize(Object document) {
    return mapper.writeValueAsString(document);
  }

  /**
   * Converts an object to a {@link JsonNode}.
   *
   * @param value the object to convert
   * @return the resulting {@link JsonNode}
   */
  public static JsonNode toJsonNode(Object value) {
    return mapper.valueToTree(value);
  }

  /**
   * Projects the given fields of the given node onto a new node. An empty fields projects the
   * entire document.
   *
   * 

Note: projection follows MongoDB's projection pattern * (https://docs.mongodb.com/manual/tutorial/project-fields-from-query-results/). The projection * is only applied to child nodes of objects. Arrays or nested arrays are iterated over and * projection picks up again when objects are found. Primitive values that are not part of a * terminal projection are removed. * * @param node the subject of the projection * @param fields dot-notated fields denoting what should be projected * @return the projected node */ public static JsonNode project(JsonNode node, Collection fields) { if (fields == null || fields.isEmpty()) { return node.deepCopy(); } if (!node.isContainerNode()) { return mapper.missingNode(); } return projectRecurse(node, createProjection(fields)); } /** * Puts the projection fields into a json structure. * * @param fields the dot-notated fields denoting what should be projected * @return a json document that corresponds to the projection */ private static JsonNode createProjection(Collection fields) { ObjectNode projection = mapper.createObjectNode(); for (String field : fields) { String[] path = field.split("\\."); ObjectNode parent = projection; for (String step : path) { parent = parent.with(step); } } return projection; } /** * Recurses through the node and its corresponding projection. Removes fields that are not part of * the projection. Does nothing if the projection is empty. * *

Note: projection follows MongoDB's projection pattern * (https://docs.mongodb.com/manual/tutorial/project-fields-from-query-results/). The projection * is only applied to child nodes of objects. Arrays or nested arrays are iterated over and * projection picks up again when objects are found. Primitive values that are not part of a * terminal projection are removed. * * @param node the subject of the projection * @param projection the projection */ private static JsonNode projectRecurse(JsonNode node, JsonNode projection) { if (projection.isMissingNode()) { return mapper.missingNode(); } else if (projection.size() == 0) { return node.deepCopy(); } else if (node.isValueNode()) { return mapper.missingNode(); } else if (node.isArray()) { ArrayNode array = mapper.createArrayNode(); new JsonField(node) .stream() .map(f -> projectRecurse(f.getValue(), projection)) .filter(n -> !n.isMissingNode()) .forEach(n -> array.add(n)); return array; } else { ObjectNode object = mapper.createObjectNode(); new JsonField(node) .fieldNames() .stream() .map(f -> new JsonField(f, projectRecurse(node.path(f), projection.path(f)))) .filter(f -> !f.getValue().isMissingNode()) .forEach(f -> object.set(f.getKey(), f.getValue())); return object; } } /** * Tests whether or not the given field is part of the field projection denoted by the given * fields. * *

Note: projection follows MongoDB's projection pattern * (https://docs.mongodb.com/manual/tutorial/project-fields-from-query-results/). * * @param field the field to test * @param fields the projection * @return if the field is part of the projection */ public static boolean matchesProjection(String field, Collection fields) { if (fields == null || fields.isEmpty()) { return true; } JsonNode projection = createProjection(fields); for (String part : field.split("\\.")) { projection = projection.path(part); if (!projection.isMissingNode() && projection.size() == 0) { return true; } } return false; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy