
com.rapleaf.jack.store.json.JsonDbHelper Maven / Gradle / Ivy
package com.rapleaf.jack.store.json;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.rapleaf.jack.store.ValueType;
public final class JsonDbHelper {
private JsonDbHelper() {
}
public static List toTupleList(JsonObject json) {
return toTupleList(Collections.emptyList(), json);
}
public static List toTupleList(List parentPaths, JsonObject json) {
List tuples = Lists.newArrayList();
Set> entrySet = json.entrySet();
if (entrySet.size() == 0) {
tuples.add(JsonDbTuple.createEmpty(parentPaths));
}
for (Map.Entry entry : entrySet) {
List childPaths = Lists.newArrayList(parentPaths);
String key = entry.getKey();
JsonElement jsonElement = entry.getValue();
if (jsonElement.isJsonArray()) {
tuples.addAll(getTupleListFromArray(parentPaths, Optional.of(key), jsonElement.getAsJsonArray()));
continue;
}
childPaths.add(new ElementPath(entry.getKey()));
if (jsonElement.isJsonPrimitive()) {
tuples.add(createPrimitiveTuple(childPaths, jsonElement.getAsJsonPrimitive()));
} else if (jsonElement.isJsonObject()) {
tuples.addAll(toTupleList(childPaths, jsonElement.getAsJsonObject()));
} else if (jsonElement.isJsonNull()) {
tuples.add(JsonDbTuple.createNull(childPaths));
} else {
throw new IllegalArgumentException("Unexpected json element: " + jsonElement);
}
}
return tuples;
}
private static List getTupleListFromArray(List parentPaths, Optional arrayName, JsonArray jsonArray) {
List tuples = Lists.newArrayListWithCapacity(jsonArray.size());
int size = jsonArray.size();
if (size == 0) {
List childPaths = Lists.newArrayList(parentPaths);
childPaths.add(new ArrayPath(arrayName, 0, 0));
tuples.add(JsonDbTuple.createEmpty(childPaths));
}
for (int i = 0; i < jsonArray.size(); i++) {
List childPaths = Lists.newArrayList(parentPaths);
childPaths.add(new ArrayPath(arrayName, i, size));
JsonElement jsonElement = jsonArray.get(i);
if (jsonElement.isJsonPrimitive()) {
tuples.add(createPrimitiveTuple(childPaths, jsonElement.getAsJsonPrimitive()));
} else if (jsonElement.isJsonArray()) {
JsonArray keylessArray = jsonElement.getAsJsonArray();
tuples.addAll(getTupleListFromArray(childPaths, Optional.empty(), keylessArray));
} else if (jsonElement.isJsonObject()) {
tuples.addAll(toTupleList(childPaths, jsonElement.getAsJsonObject()));
} else if (jsonElement.isJsonNull()) {
tuples.add(JsonDbTuple.createNull(childPaths));
} else {
throw new IllegalArgumentException("Unexpected json element: " + jsonElement);
}
}
return tuples;
}
private static JsonDbTuple createPrimitiveTuple(List childPaths, JsonPrimitive jsonPrimitive) {
if (jsonPrimitive.isBoolean()) {
return JsonDbTuple.createBoolean(childPaths, jsonPrimitive.getAsString());
} else if (jsonPrimitive.isNumber()) {
return JsonDbTuple.createNumber(childPaths, jsonPrimitive.getAsString());
} else {
return JsonDbTuple.createString(childPaths, jsonPrimitive.getAsString());
}
}
public static JsonObject fromTupleList(List tuples) {
JsonObject json = new JsonObject();
for (JsonDbTuple tuple : tuples) {
processTuple(json, tuple.getPaths(), tuple.getType(), tuple.getValue());
}
return json;
}
private static void processTuple(JsonElement parentElement, List paths, ValueType type, String value) {
Preconditions.checkArgument(!paths.isEmpty());
TuplePath childPath = paths.get(0);
if (childPath.isArray()) {
addArrayPath(parentElement, (ArrayPath)childPath, paths.subList(1, paths.size()), type, value);
} else {
addElementPath(parentElement, (ElementPath)childPath, paths.subList(1, paths.size()), type, value);
}
}
private static void addArrayPath(JsonElement parentElement, ArrayPath childPath, List tailPaths, ValueType type, String value) {
Optional childName = childPath.getName();
Optional childIndex = childPath.getListIndex();
Optional childSize = childPath.getListSize();
Preconditions.checkState(childIndex.isPresent());
Preconditions.checkState(childSize.isPresent());
if (childName.isPresent()) {
// when the child has a name, the parent must be an object
String name = childName.get();
Preconditions.checkState(parentElement.isJsonObject());
JsonObject parentObject = parentElement.getAsJsonObject();
final JsonArray childArray;
if (!parentObject.has(name)) {
childArray = new JsonArray();
parentObject.add(name, childArray);
} else {
childArray = parentObject.get(name).getAsJsonArray();
}
if (!tailPaths.isEmpty()) {
addChildPathToParentArray(childArray, childIndex.get(), tailPaths, type, value);
} else {
// tuples are sorted by name, and this element must have not been added to the array
Preconditions.checkState(childArray.size() == childIndex.get());
if (type != ValueType.JSON_EMPTY) {
childArray.add(getJsonElement(type, value));
} else if (childSize.get() == 0) {
// when tail paths is empty and child size is zero,
// it is an empty array and can be ignored: []
} else {
// when tail paths is empty and child size is not zero,
// it is an empty object inside an array: [..., {}, ...]
childArray.add(new JsonObject());
}
}
} else {
// when the child has no name, the parent must be an array
Preconditions.checkState(parentElement.isJsonArray());
JsonArray parentArray = parentElement.getAsJsonArray();
if (!tailPaths.isEmpty()) {
addChildPathToParentArray(parentArray, childIndex.get(), tailPaths, type, value);
} else {
// tuples are sorted by name, and this element must have not been added to the array
Preconditions.checkState(parentArray.size() == childIndex.get());
if (type != ValueType.JSON_EMPTY) {
parentArray.add(getJsonElement(type, value));
}
}
}
}
private static void addElementPath(JsonElement parentElement, ElementPath childPath, List tailPaths, ValueType type, String value) {
// parent element must be an object because it has an element path
Preconditions.checkState(parentElement.isJsonObject());
// child path must have a name because it is an element path
Optional childName = childPath.getName();
Preconditions.checkState(childName.isPresent());
final JsonObject parentObject = parentElement.getAsJsonObject();
if (!tailPaths.isEmpty()) {
addElementChildPathToParentObject(parentObject, childName.get(), tailPaths, type, value);
} else {
parentObject.add(childName.get(), getJsonElement(type, value));
}
}
private static void addElementChildPathToParentObject(JsonObject parentObject, String childPathName, List tailPaths, ValueType type, String value) {
TuplePath nextChildPath = tailPaths.get(0);
final JsonElement childElement;
if (!parentObject.has(childPathName)) {
// current child must be an object because it is an element path
childElement = new JsonObject();
// next child must have a name because the current child is an object
Preconditions.checkArgument(nextChildPath.getName().isPresent());
parentObject.add(childPathName, childElement);
} else {
childElement = parentObject.get(childPathName);
}
processTuple(childElement, tailPaths, type, value);
}
private static void addChildPathToParentArray(JsonArray parentArray, int childPathIndex, List tailPaths, ValueType type, String value) {
TuplePath nextChildPath = tailPaths.get(0);
final JsonElement childElement;
if (parentArray.size() <= childPathIndex) {
if (nextChildPath.getName().isPresent() || !nextChildPath.isArray()) {
childElement = new JsonObject();
} else {
childElement = new JsonArray();
}
parentArray.add(childElement);
} else {
childElement = parentArray.get(childPathIndex);
}
processTuple(childElement, tailPaths, type, value);
}
private static JsonElement getJsonElement(ValueType type, String value) {
switch (type) {
case JSON_STRING:
return new JsonPrimitive(value);
case JSON_BOOLEAN:
return new JsonPrimitive(Boolean.valueOf(value));
case JSON_NUMBER:
try {
return new JsonPrimitive(Long.valueOf(value));
} catch (NumberFormatException e1) {
try {
return new JsonPrimitive(Double.valueOf(value));
} catch (NumberFormatException e2) {
throw new IllegalArgumentException("Unexpected json number: " + value);
}
}
case JSON_NULL:
return JsonNull.INSTANCE;
case JSON_EMPTY:
return new JsonObject();
default:
throw new IllegalArgumentException("Unexpected value type: " + type.name());
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy