com.flipkart.zjsonpatch.JsonPatch Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of zjsonpatch Show documentation
Show all versions of zjsonpatch Show documentation
Java Library to find / apply JSON Patches according to RFC 6902
/*
* 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 com.flipkart.zjsonpatch;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.NullNode;
import com.google.common.base.Function;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
/**
* User: gopi.vishwakarma
* Date: 31/07/14
*/
public final class JsonPatch {
private static final DecodePathFunction DECODE_PATH_FUNCTION = new DecodePathFunction();
private JsonPatch() {}
private final static class DecodePathFunction implements Function {
@Override
public String apply(String path) {
return path.replaceAll("~1", "/").replaceAll("~0", "~"); // see http://tools.ietf.org/html/rfc6901#section-4
}
}
private static JsonNode getPatchAttr(JsonNode jsonNode, String attr) {
JsonNode child = jsonNode.get(attr);
if (child == null)
throw new InvalidJsonPatchException("Invalid JSON Patch payload (missing '" + attr + "' field)");
return child;
}
private static JsonNode getPatchAttrWithDefault(JsonNode jsonNode, String attr, JsonNode defaultValue) {
JsonNode child = jsonNode.get(attr);
if (child == null)
return defaultValue;
else
return child;
}
private static void process(JsonNode patch, JsonPatchProcessor processor, EnumSet flags)
throws InvalidJsonPatchException {
if (!patch.isArray())
throw new InvalidJsonPatchException("Invalid JSON Patch payload (not an array)");
Iterator operations = patch.iterator();
while (operations.hasNext()) {
JsonNode jsonNode = operations.next();
if (!jsonNode.isObject()) throw new InvalidJsonPatchException("Invalid JSON Patch payload (not an object)");
Operation operation = Operation.fromRfcName(getPatchAttr(jsonNode, Constants.OP).toString().replaceAll("\"", ""));
List path = getPath(getPatchAttr(jsonNode, Constants.PATH));
switch (operation) {
case REMOVE: {
processor.remove(path);
break;
}
case ADD: {
JsonNode value;
if (!flags.contains(CompatibilityFlags.MISSING_VALUES_AS_NULLS))
value = getPatchAttr(jsonNode, Constants.VALUE);
else
value = getPatchAttrWithDefault(jsonNode, Constants.VALUE, NullNode.getInstance());
processor.add(path, value.deepCopy());
break;
}
case REPLACE: {
JsonNode value;
if (!flags.contains(CompatibilityFlags.MISSING_VALUES_AS_NULLS))
value = getPatchAttr(jsonNode, Constants.VALUE);
else
value = getPatchAttrWithDefault(jsonNode, Constants.VALUE, NullNode.getInstance());
processor.replace(path, value.deepCopy());
break;
}
case MOVE: {
List fromPath = getPath(getPatchAttr(jsonNode, Constants.FROM));
processor.move(fromPath, path);
break;
}
case COPY: {
List fromPath = getPath(getPatchAttr(jsonNode, Constants.FROM));
processor.copy(fromPath, path);
break;
}
case TEST: {
JsonNode value;
if (!flags.contains(CompatibilityFlags.MISSING_VALUES_AS_NULLS))
value = getPatchAttr(jsonNode, Constants.VALUE);
else
value = getPatchAttrWithDefault(jsonNode, Constants.VALUE, NullNode.getInstance());
processor.test(path, value.deepCopy());
break;
}
}
}
}
public static void validate(JsonNode patch, EnumSet flags) throws InvalidJsonPatchException {
process(patch, NoopProcessor.INSTANCE, flags);
}
public static void validate(JsonNode patch) throws InvalidJsonPatchException {
validate(patch, CompatibilityFlags.defaults());
}
public static JsonNode apply(JsonNode patch, JsonNode source, EnumSet flags) throws JsonPatchApplicationException {
CopyingApplyProcessor processor = new CopyingApplyProcessor(source);
process(patch, processor, flags);
return processor.result();
}
public static JsonNode apply(JsonNode patch, JsonNode source) throws JsonPatchApplicationException {
return apply(patch, source, CompatibilityFlags.defaults());
}
public static void applyInPlace(JsonNode patch, JsonNode source){
applyInPlace(patch, source, CompatibilityFlags.defaults());
}
public static void applyInPlace(JsonNode patch, JsonNode source, EnumSet flags){
InPlaceApplyProcessor processor = new InPlaceApplyProcessor(source);
process(patch, processor, flags);
}
private static List getPath(JsonNode path) {
List paths = Splitter.on('/').splitToList(path.toString().replaceAll("\"", ""));
return Lists.newArrayList(Iterables.transform(paths, DECODE_PATH_FUNCTION));
}
}