![JAR search and dependency download from the Maven repository](/logo.png)
net.hamnaberg.json.pointer.JsonPointer Maven / Gradle / Ivy
package net.hamnaberg.json.pointer;
import net.hamnaberg.json.Folder;
import net.hamnaberg.json.Json;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
public final class JsonPointer {
private final List path;
public static JsonPointer compile(String pattern) {
if (pattern == null || pattern.trim().isEmpty()) {
return new JsonPointer(List.of());
}
return new JsonPointer(new JsonPointerParser().parse(pattern));
}
private JsonPointer(List path) {
this.path = path;
}
@Override
public String toString() {
if (path.isEmpty()) return "";
return path.stream().map(ref -> ref.fold(
r -> String.valueOf(r.index),
r -> escape(r.name),
() -> "-"
)).collect(Collectors.joining("/", "/", ""));
}
private String escape(String str) {
return str.replace("~", "~0").replace("/", "~1");
}
public Optional select(Json.JValue value) {
if (path.isEmpty()) {
return Optional.of(value);
}
final Iterator iterator = path.iterator();
Json.JValue current = value;
while (iterator.hasNext()) {
Ref ref = iterator.next();
final Json.JValue c = current;
current = ref.fold(
arrayRef -> foldToJson(
c,
obj -> obj.get(String.valueOf(arrayRef.index)).orElse(obj),
arr -> arr.get(arrayRef.index).orElse(arr)
),
propertyRef -> foldToJson(
c,
obj -> obj.get(String.valueOf(propertyRef.name)).orElse(obj),
Json.JValue::asJValue
),
() -> {
throw new IllegalStateException("List index is out-of-bounds");
}
);
if (!iterator.hasNext() && current != value) {
return Optional.of(current);
}
}
return Optional.empty();
}
public Json.JValue add(Json.JValue json, Json.JValue value) {
if (path.isEmpty()) {
return value;
}
Iterator iterator = path.iterator();
return addImpl(iterator, iterator.next(), json, value);
}
public Json.JValue remove(Json.JValue json) {
if (path.isEmpty()) {
return json;
}
Iterator iterator = path.iterator();
return updateImpl(iterator, iterator.next(), json, Optional.empty());
}
public Json.JValue replace(Json.JValue json, Json.JValue value) {
if (path.isEmpty()) {
return value;
}
Iterator iterator = path.iterator();
return updateImpl(iterator, iterator.next(), json, Optional.of(value));
}
public Json.JValue copy(Json.JValue json, JsonPointer from) {
//TODO: Optimize? we have 3 traversals of the graph
Optional selected = from.select(json);
return selected.map(v -> add(json, v)).orElse(json);
}
public Json.JValue move(Json.JValue json, JsonPointer from) {
//TODO: Optimize? we have 3 traversals of the graph
Optional selected = from.select(json);
return selected.map(v -> add(from.remove(json), v)).orElse(json);
}
public boolean test(Json.JValue json, Json.JValue value) {
return select(json).map(value::equals).orElse(false);
}
private Json.JValue foldToJson(Json.JValue value, Function fObject, Function fArray) {
return value.fold(Folder.from(
Json.JValue::asJValue,
Json.JValue::asJValue,
Json.JValue::asJValue,
fObject,
fArray,
Json::jNull
));
}
private Json.JValue updateImpl(Iterator path, Ref ref, Json.JValue context, Optional updateValue) {
return ref.fold(
arrayRef -> foldToJson(
context,
obj -> replaceObject(path, context, updateValue, String.valueOf(arrayRef.index)),
arr -> {
int index = arrayRef.index;
Json.JArray array = context.asJsonArrayOrEmpty();
if (!path.hasNext()) {
if (updateValue.isPresent()) {
array.get(index).orElseThrow(() -> new IllegalStateException("No value at index: " + index));
return array.replace(index, updateValue.get());
} else {
return array.remove(index);
}
} else {
Json.JValue value = array.get(index).orElseThrow(() -> new IllegalStateException("No value at index: " + index));
return array.replace(index, updateImpl(path, path.next(), value, updateValue));
}
}
),
propertyRef -> foldToJson(
context,
obj -> replaceObject(path, context, updateValue, propertyRef.name),
Json.JValue::asJValue
),
() -> {
throw new IllegalStateException("List index is out-of-bounds");
}
);
}
private Json.JValue replaceObject(Iterator path, Json.JValue context, Optional updateValue, String name) {
Json.JObject object = context.asJsonObjectOrEmpty();
if (!path.hasNext()) {
return updateValue.map(jValue -> object.put(name, jValue)).orElseGet(() -> object.remove(name));
} else {
Json.JValue value = object.get(name).orElseThrow(() -> new IllegalStateException("No value with name: " + name));
return object.put(name, updateImpl(path, path.next(), value, updateValue));
}
}
private Json.JValue addImpl(Iterator path, Ref ref, Json.JValue context, Json.JValue valueToInsert) {
return ref.fold(
arrayRef -> foldToJson(
context,
obj -> {
String name = String.valueOf(arrayRef.index);
return addObject(path, obj, valueToInsert, name);
},
arr -> {
int index = arrayRef.index;
if (!path.hasNext()) {
if (arr.size() >= index) {
return arr.insert(index, valueToInsert);
} else {
throw new IllegalStateException(String.format("List index %s is out-of-bounds", index));
}
} else {
Json.JValue value = arr.get(index).orElseThrow(() -> new IllegalStateException(String.format("List index %s is out-of-bounds", index)));
return arr.replace(index, addImpl(path, path.next(), value, valueToInsert));
}
}
),
propertyRef -> foldToJson(
context,
obj -> addObject(path, obj, valueToInsert, propertyRef.name),
Json.JValue::asJValue
),
() ->
foldToJson(
context,
j -> j,
arr -> {
if (path.hasNext()) {
throw new IllegalStateException("Nonsense to have more values after a end-of-array");
}
return arr.append(valueToInsert);
}
)
);
}
private Json.JValue addObject(Iterator path, Json.JValue context, Json.JValue valueToInsert, String name) {
Json.JObject object = context.asJsonObjectOrEmpty();
if (!path.hasNext()) {
return object.put(name, valueToInsert);
} else {
Json.JValue value = object.get(name).orElseThrow(() -> new IllegalStateException("No value with name: " + name));
return object.put(name, addImpl(path, path.next(), value, valueToInsert));
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy