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

io.fabric8.zjsonpatch.JsonPatch Maven / Gradle / Ivy

/**
 * Copyright 2016 flipkart.com zjsonpatch.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.fabric8.zjsonpatch;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import static io.fabric8.zjsonpatch.internal.guava.Strings.isNullOrEmpty;

/**
 * User: gopi.vishwakarma
 * Date: 31/07/14
 */
public final class JsonPatch {

  private JsonPatch() {
  }

  public static JsonNode apply(JsonNode patch, JsonNode source) {
    Iterator operations = patch.iterator();
    JsonNode ret = source.deepCopy();
    while (operations.hasNext()) {
      JsonNode jsonNode = operations.next();
      Operation operation = Operation.fromRfcName(jsonNode.get(Constants.OP).toString().replaceAll("\"", ""));
      List path = getPath(jsonNode.get(Constants.PATH));
      List fromPath = null;
      if (Operation.MOVE.equals(operation)) {
        fromPath = getPath(jsonNode.get(Constants.FROM));
      }
      JsonNode value = null;
      if (!Operation.REMOVE.equals(operation) && !Operation.MOVE.equals(operation)) {
        value = jsonNode.get(Constants.VALUE);
      }

      switch (operation) {
        case REMOVE:
          remove(ret, path);
          break;
        case REPLACE:
          ret = replace(ret, path, value);
          break;
        case ADD:
          ret = add(ret, path, value);
          break;
        case MOVE:
          ret = move(ret, fromPath, path);
          break;
      }
    }
    return ret;
  }

  private static JsonNode move(JsonNode node, List fromPath, List toPath) {
    JsonNode parentNode = getParentNode(node, fromPath);
    String field = fromPath.get(fromPath.size() - 1).replaceAll("\"", "");
    JsonNode valueNode = parentNode.isArray() ? parentNode.get(Integer.parseInt(field)) : parentNode.get(field);
    remove(node, fromPath);
    return add(node, toPath, valueNode);
  }

  private static JsonNode add(JsonNode node, List path, JsonNode value) {
    if (path.isEmpty()) {
      throw new RuntimeException("[ADD Operation] path is empty , path : ");
    } else {
      JsonNode parentNode = getParentNode(node, path);
      if (parentNode == null) {
        throw new RuntimeException("[ADD Operation] noSuchPath in source, path provided : " + path);
      } else {
        String fieldToReplace = path.get(path.size() - 1).replaceAll("\"", "");
        if (fieldToReplace.equals("") && path.size() == 1) {
          return value;
        }
        if (!parentNode.isContainerNode()) {
          throw new RuntimeException("[ADD Operation] parent is not a container in source, path provided : " + path + " | node : " + parentNode);
        } else {
          if (parentNode.isArray()) {
            addToArray(path, value, parentNode);
          } else {
            addToObject(path, parentNode, value);
          }
        }
      }
    }
    return node;
  }

  private static void addToObject(List path, JsonNode node, JsonNode value) {
    final ObjectNode target = (ObjectNode) node;
    String key = path.get(path.size() - 1).replaceAll("\"", "");
    target.put(key, value);
  }

  private static void addToArray(List path, JsonNode value, JsonNode parentNode) {
    final ArrayNode target = (ArrayNode) parentNode;
    String idxStr = path.get(path.size() - 1);

    if ("-".equals(idxStr)) {
      // see http://tools.ietf.org/html/rfc6902#section-4.1
      target.add(value);
    } else {
      Integer idx = Integer.parseInt(idxStr.replaceAll("\"", ""));
      if (idx < target.size()) {
        target.insert(idx, value);
      } else {
        if (idx == target.size()) {
          target.add(value);
        } else {
          throw new RuntimeException("[ADD Operation] [addToArray] index Out of bound, index provided is higher than allowed, path " + path);
        }
      }
    }
  }

  private static JsonNode replace(JsonNode node, List path, JsonNode value) {
    if (path.isEmpty()) {
      throw new RuntimeException("[Replace Operation] path is empty");
    } else {
      JsonNode parentNode = getParentNode(node, path);
      if (parentNode == null) {
        throw new RuntimeException("[Replace Operation] noSuchPath in source, path provided : " + path);
      } else {
        String fieldToReplace = path.get(path.size() - 1).replaceAll("\"", "");
        if (isNullOrEmpty(fieldToReplace) && path.size() == 1) {
          return value;
        }
        if (parentNode.isObject())
          ((ObjectNode) parentNode).put(fieldToReplace, value);
        else
          ((ArrayNode) parentNode).set(Integer.parseInt(fieldToReplace), value);
      }
      return node;
    }
  }

  private static void remove(JsonNode node, List path) {
    if (path.isEmpty()) {
      throw new RuntimeException("[Remove Operation] path is empty");
    } else {
      JsonNode parentNode = getParentNode(node, path);
      if (parentNode == null) {
        throw new RuntimeException("[Remove Operation] noSuchPath in source, path provided : " + path);
      } else {
        String fieldToRemove = path.get(path.size() - 1).replaceAll("\"", "");
        if (parentNode.isObject())
          ((ObjectNode) parentNode).remove(fieldToRemove);
        else
          ((ArrayNode) parentNode).remove(Integer.parseInt(fieldToRemove));
      }
    }
  }

  private static JsonNode getParentNode(JsonNode node, List fromPath) {
    List pathToParent = fromPath.subList(0, fromPath.size() - 1); // would never by out of bound, lets see
    return getNode(node, pathToParent, 1);
  }

  private static JsonNode getNode(JsonNode ret, List path, int pos) {
    if (pos >= path.size()) {
      return ret;
    }
    String key = path.get(pos);
    if (ret.isArray()) {
      int keyInt = Integer.parseInt(key.replaceAll("\"", ""));
      return getNode(ret.get(keyInt), path, ++pos);
    } else if (ret.isObject()) {
      if (ret.has(key)) {
        return getNode(ret.get(key), path, ++pos);
      }
      return null;
    } else {
      return ret;
    }
  }

  private static List getPath(JsonNode path) {
    String pathString = path.toString().replaceAll("\"", "");
    String[] paths = pathString.split("/");

    if (paths.length == 0) {
      return Arrays.asList("", "");
    }

    List pathList = new ArrayList<>();
    for (String p : paths) {
      pathList.add(p.replaceAll("~1", "/").replaceAll("~0", "~"));
    }
    if (pathString.endsWith("/")) {
      pathList.add("");
    }
    return Collections.unmodifiableList(pathList);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy