entaxy-public/platform/runtime/base/base-support/src/main/java/ru/entaxy/platform/base/support/JSONUtils.java

708 lines
20 KiB
Java

/* ~~~~~~licensing~~~~~~
* base-support
* ==========
* Copyright (C) 2020 - 2023 EmDev LLC
* ==========
* You may not use this file except in accordance with the License Terms of the Copyright
* Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
* rights to the Software and any copies are the property of the Copyright Holder. Unless
* it is explicitly allowed the Copyright Holder, the User is prohibited from using the
* Software for commercial purposes to provide services to third parties.
*
* The Copyright Holder hereby declares that the Software is provided on an "AS IS".
* Under no circumstances does the Copyright Holder guarantee or promise that the
* Software provided by him will be suitable or not suitable for the specific purposes
* of the User, that the Software will meet all commercial and personal subjective
* expectations of the User, that the Software will work properly, without technical
* errors, quickly and uninterruptedly.
*
* Under no circumstances shall the Copyright Holder or its Affiliates is not liable
* to the User for any direct or indirect losses of the User, his expenses or actual
* damage, including, downtime; loss of bussines; lost profit; lost earnings; loss
* or damage to data, property, etc.
* ~~~~~~/licensing~~~~~~
*/
package ru.entaxy.platform.base.support;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
public class JSONUtils {
public static final String PROP_VALUE = "_value";
public static Map<String, Object> element2map(JsonElement element){
Map<String, Object> result = new HashMap<>();
if (element.isJsonObject()) {
JsonObject jsonObject = element.getAsJsonObject();
for (Entry<String, JsonElement> entry: jsonObject.entrySet()) {
result.put(entry.getKey(), element2object(entry.getValue()));
}
} else
if (element.isJsonArray()) {
JsonArray array = element.getAsJsonArray();
result.put(PROP_VALUE, element2list(element));
} else
if (element.isJsonNull()) {
result.put(PROP_VALUE, null);
} else
if (element.isJsonPrimitive()) {
result.put(PROP_VALUE, element2value(element));
}
return result;
}
public static Object element2object(JsonElement element) {
if (element.isJsonNull() || element.isJsonPrimitive())
return element2value(element);
if (element.isJsonArray())
return element2list(element);
if (element.isJsonObject())
return element2map(element);
return null;
}
public static Object element2value(JsonElement element) {
Object result = null;
if (element.isJsonNull() || !element.isJsonPrimitive())
return result;
try {
JsonPrimitive primitive = element.getAsJsonPrimitive();
if (primitive.isNumber())
result = primitive.getAsNumber();
else if (primitive.isBoolean())
result = primitive.getAsBoolean();
else result = primitive.getAsString();
} catch (Exception e1) {
try {
result = element.getAsBoolean();
} catch (Exception e2) {
result = element.getAsString();
}
}
return result;
}
public static List<Object> element2list(JsonElement element) {
List<Object> result = new ArrayList<>();
JsonArray array = element.getAsJsonArray();
for (int i=0; i<array.size(); i++)
result.add(element2object(array.get(i)));
return result;
}
public static String getJsonRootObjectString(URL url) throws IOException {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(
url.openStream(), StandardCharsets.UTF_8))){
String data = reader
.lines()
.collect(Collectors.joining("\n"));
return data;
}
}
public static JsonObject getJsonRootObject(URL url) throws IOException {
return JSONUtils.getJsonRootObject(getJsonRootObjectString(url));
}
public static JsonObject getJsonRootObject(String jsonData) {
try {
JsonElement je = (new JsonParser()).parse(jsonData);
JsonObject root = je.getAsJsonObject();
return root;
} catch (Exception e) {
return new JsonObject();
}
}
public static JsonObject getJsonRootObjectUnsafe(String jsonData) throws Exception {
JsonElement je = (new JsonParser()).parse(jsonData);
JsonObject root = je.getAsJsonObject();
return root;
}
public static void mergeObjects(JsonObject source, JsonObject target) {
if (source == null)
return;
if (target == null)
return;
for (Entry<String, JsonElement> entry: source.entrySet()) {
if (target.has(entry.getKey())) {
if (entry.getValue().isJsonObject())
if (target.get(entry.getKey()).isJsonObject()) {
mergeObjects(entry.getValue().getAsJsonObject(), target.get(entry.getKey()).getAsJsonObject());
continue;
}
target.remove(entry.getKey());
}
target.add(entry.getKey(), entry.getValue().deepCopy());
}
}
public static boolean replaceValue(JsonObject origin, String path, JsonElement replacement) {
return setValue(origin, path, replacement, false);
}
public static boolean setValue(JsonObject origin, String path, JsonElement replacement, boolean ifMissing) {
String preparedPath = path.replaceAll("(\\[\\d+\\])", ".$1");
String[] pathSplitted = preparedPath.split("\\.");
JsonElement currentElement = origin;
for (int i=0; i<pathSplitted.length-1; i++) {
String fragment = pathSplitted[i];
if (fragment.startsWith("[") && fragment.endsWith("]")) {
// array index
if (currentElement.isJsonArray()) {
int index = Integer.parseInt(fragment.substring(1, fragment.length()-1));
JsonArray arr = currentElement.getAsJsonArray();
if (arr.size()>index)
currentElement = arr.get(index);
else {
// TODO process
// System.out.println("ERROR: index out of bounds");
}
} else {
// TODO process
// System.out.println("ERROR: found indexed property on non-array value");
}
} else if (currentElement.isJsonObject()) {
currentElement = currentElement.getAsJsonObject().get(fragment);
} else {
// TODO process
// System.out.println("ERROR: currentElement can't be traversed");
}
}
if (currentElement == null) {
// System.out.println("ERROR: currentElement is null");
return false;
}
String finalFragment = pathSplitted[pathSplitted.length-1];
if (finalFragment.startsWith("[") && finalFragment.endsWith("]")) {
// array index
if (currentElement.isJsonArray()) {
// System.out.println("INDEX: [" + finalFragment.substring(1, finalFragment.length()-1) + "]");
int index = Integer.parseInt(finalFragment.substring(1, finalFragment.length()-1));
JsonArray arr = currentElement.getAsJsonArray();
if (arr.size()>index) {
// arr.remove(index);
arr.set(index, replacement);
} else {
// TODO process
// System.out.println("ERROR: index out of bounds");
}
} else {
// TODO process
// System.out.println("ERROR: found indexed property on non-array value");
}
} else if (currentElement.isJsonObject()) {
if (ifMissing && currentElement.getAsJsonObject().has(finalFragment))
return false;
currentElement.getAsJsonObject().remove(finalFragment);
currentElement.getAsJsonObject().add(finalFragment, replacement);
} else {
// System.out.println("ERROR: currentElement can't be traversed");
return false;
}
// System.out.println("\n -- found --\n" + currentElement.toString() + "\n");
return true;
}
@Deprecated(forRemoval = true, since = "1.9")
public static JsonElement findElement(JsonObject jsonObject, String pathFragment) {
JsonElement result = findElement(jsonObject, pathFragment, "");
return result;
}
public static JsonElement findElementExt(JsonObject jsonObject, String pathFragment) {
JsonElement result = null;
if (pathFragment.startsWith("?.")) {
result = findElement(jsonObject, pathFragment.substring(2), "");
} else {
result = getElement(jsonObject, pathFragment);
}
return result;
}
public static JsonElement findElement(JsonObject jsonObject, String pathFragment, String parentPath) {
for (Entry<String, JsonElement> entry: jsonObject.entrySet()) {
String currentPath = parentPath + "." + entry.getKey();
if (currentPath.endsWith(pathFragment))
return entry.getValue();
}
for (Entry<String, JsonElement> entry: jsonObject.entrySet()) {
if (!entry.getValue().isJsonObject())
continue;
String currentPath = parentPath + "." + entry.getKey();
JsonElement currentResult = findElement(entry.getValue().getAsJsonObject(), pathFragment, currentPath);
if (currentResult != null)
return currentResult;
}
return null;
}
public static JsonElement getElement(JsonObject jsonObject, String pathFragment) {
int ind = 0;
String currentFragment = "";
String newFragment = null;
if (pathFragment.startsWith("'")) {
int closing = pathFragment.indexOf("'", 1);
if (closing>1) {
currentFragment = pathFragment.substring(1, closing);
ind = pathFragment.indexOf('.', closing);
if (ind > 0 ) {
newFragment = pathFragment.substring(ind+1);
} else {
newFragment = null;
}
}
} else {
ind = pathFragment.indexOf(".");
if (ind > 0) {
currentFragment = pathFragment.substring(0, ind);
newFragment = pathFragment.substring(ind+1);
} else {
currentFragment = pathFragment;
newFragment = null;
}
}
if (!jsonObject.has(currentFragment))
return null;
JsonElement je = jsonObject.get(currentFragment);
if (newFragment == null)
return je;
if (!je.isJsonObject())
return null;
return getElement(je.getAsJsonObject(), newFragment);
}
public static class JsonTraverse {
List<ObjectChecker> checkers = new ArrayList<>();
public JsonTraverse checker(ObjectChecker checker) {
this.checkers.add(checker);
return this;
}
public Object traverse(JsonObject rootObject) {
Object result = null;
Map<String, Object> context = new HashMap<>();
result = element2object(rootObject, context, "$");
return result;
}
protected Object element2object(JsonElement element, Map<String, Object> context, String path) {
if (element.isJsonNull() || element.isJsonPrimitive())
return element2value(element, context, path);
if (element.isJsonArray())
return element2list(element, context, path);
if (element.isJsonObject()) {
JsonObject jsonObject = element.getAsJsonObject();
ObjectWrapper ow = null;
for (ObjectChecker oc: checkers) {
ow = oc.checkObject(jsonObject);
if (ow != null)
break;
}
if (ow != null) {
ow.wrap(jsonObject, context, path);
if (ow.continueTraverse())
ow.setTraverseMap(element2map(element, context, path), context);
return ow;
}
return element2map(element, context, path);
}
return null;
}
protected Map<String, Object> element2map(JsonElement element, Map<String, Object> context, String path){
Map<String, Object> result = new HashMap<>();
if (element.isJsonObject()) {
JsonObject jsonObject = element.getAsJsonObject();
for (Entry<String, JsonElement> entry: jsonObject.entrySet()) {
result.put(entry.getKey(), element2object(entry.getValue(), context, path + "." + entry.getKey()));
}
} else
if (element.isJsonArray()) {
JsonArray array = element.getAsJsonArray();
result.put(PROP_VALUE, element2list(element, context, path));
} else
if (element.isJsonNull()) {
result.put(PROP_VALUE, null);
} else
if (element.isJsonPrimitive()) {
result.put(PROP_VALUE, element2value(element, context, path));
}
return result;
}
protected Object element2value(JsonElement element, Map<String, Object> context, String path) {
Object result = null;
if (element.isJsonNull() || !element.isJsonPrimitive())
return result;
try {
JsonPrimitive primitive = element.getAsJsonPrimitive();
if (primitive.isNumber())
result = primitive.getAsNumber();
else if (primitive.isBoolean())
result = primitive.getAsBoolean();
else result = primitive.getAsString();
} catch (Exception e1) {
try {
result = element.getAsBoolean();
} catch (Exception e2) {
result = element.getAsString();
}
}
return result;
}
protected List<Object> element2list(JsonElement element, Map<String, Object> context, String path) {
List<Object> result = new ArrayList<>();
JsonArray array = element.getAsJsonArray();
for (int i=0; i<array.size(); i++)
result.add(element2object(array.get(i), context, path + "[" + i + "]"));
return result;
}
}
public static abstract class ObjectChecker {
public abstract ObjectWrapper checkObject(JsonObject object);
}
public static abstract class ObjectWrapper implements Map<String, Object> {
protected Map<String, Object> data = new HashMap<>();
public abstract void wrap(JsonObject object, Map<String, Object> context, String path);
public boolean continueTraverse() {
return true;
}
public void setTraverseMap(Map<String, Object> traverseMap, Map<String, Object> context) {
this.data = traverseMap;
}
@Override
public int size() {
return this.data.size();
}
@Override
public boolean isEmpty() {
return this.data.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return this.data.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return this.data.containsValue(value);
}
@Override
public Object get(Object key) {
return this.data.get(key);
}
@Override
public Object put(String key, Object value) {
return this.data.put(key, value);
}
@Override
public Object remove(Object key) {
return this.data.remove(key);
}
@Override
public void putAll(Map<? extends String, ? extends Object> m) {
this.data.putAll(m);
}
@Override
public void clear() {
this.data.clear();
}
@Override
public Set<String> keySet() {
return this.data.keySet();
}
@Override
public Collection<Object> values() {
return this.data.values();
}
@Override
public Set<Entry<String, Object>> entrySet() {
return this.data.entrySet();
};
}
public static class JsonBuilder {
public static JsonObjectBuilder create(JsonObject jsonObject) {
return new JsonObjectBuilder(jsonObject);
}
protected static abstract class CommonObjectBuilder<T extends CommonObjectBuilder<T>> {
protected JsonObject currentObject;
protected CommonObjectBuilder(JsonObject jsonObject) {
this.currentObject = jsonObject;
}
protected T me() {
return (T) this;
}
public T string(String name, String value) {
return this.string(name, value, true);
}
public T string(String name, String value, boolean replace) {
JSONUtils.setValue(currentObject, name, new JsonPrimitive(value), replace);
return me();
}
public T bool(String name, boolean value) {
return this.bool(name, value, true);
}
public T bool(String name, boolean value, boolean replace) {
JSONUtils.setValue(currentObject, name, new JsonPrimitive(value), replace);
return me();
}
public T number(String name, Number value) {
return this.number(name, value, true);
}
public T number(String name, Number value, boolean replace) {
JSONUtils.setValue(currentObject, name, new JsonPrimitive(value), replace);
return me();
}
public JsonArrayBuilder<T> array(String name) {
return this.array(name, true);
}
public JsonArrayBuilder<T> array(String name, boolean replace) {
if (replace)
this.currentObject.remove(name);
if (!this.currentObject.has(name)) {
this.currentObject.add(name, new JsonArray());
}
JsonElement je = this.currentObject.get(name);
if (!je.isJsonArray()) {
this.currentObject.remove(name);
this.currentObject.add(name, new JsonArray());
}
return new JsonArrayBuilder<T>(this.currentObject.get(name).getAsJsonArray(), me());
}
protected JsonObject getCreateobject(String name, boolean replace) {
if (replace)
this.currentObject.remove(name);
if (!this.currentObject.has(name)) {
this.currentObject.add(name, new JsonObject());
}
JsonElement je = this.currentObject.get(name);
if (!je.isJsonObject()) {
this.currentObject.remove(name);
this.currentObject.add(name, new JsonObject());
}
return this.currentObject.get(name).getAsJsonObject();
};
}
public static class JsonObjectBuilder extends CommonObjectBuilder<JsonObjectBuilder> {
protected JsonObjectBuilder parentBuilder = null;
protected JsonObjectBuilder(JsonObject jsonObject) {
super(jsonObject);
}
protected JsonObjectBuilder(JsonObject jsonObject, JsonObjectBuilder parent) {
this(jsonObject);
this.parentBuilder = parent;
}
public JsonObjectBuilder object(String name) {
return this.object(name, true);
}
public JsonObjectBuilder object(String name, boolean replace) {
return new JsonObjectBuilder(getCreateobject(name, replace), this);
}
public JsonObjectBuilder up() {
return this.parentBuilder;
}
}
public static class JsonObjectInArrayBuilder<T extends AbstractJsonArrayBuilder<T>> extends CommonObjectBuilder<JsonObjectInArrayBuilder<T>> {
protected T parentBuilder = null;
protected JsonObjectInArrayBuilder(JsonObject jsonObject, T parent) {
super(jsonObject);
this.parentBuilder = parent;
}
public JsonObjectBuilder object(String name) {
return this.object(name, true);
}
public JsonObjectBuilder object(String name, boolean replace) {
return new JsonObjectBuilder(getCreateobject(name, replace));
}
public T up() {
return this.parentBuilder;
}
}
protected static class AbstractJsonArrayBuilder<T extends AbstractJsonArrayBuilder<T>> {
JsonArray currentArray;
protected AbstractJsonArrayBuilder(JsonArray jsonArray) {
this.currentArray = jsonArray;
}
protected T me() {
return (T) this;
}
public T clear() {
for (int i=0; i<this.currentArray.size(); i++)
this.currentArray.remove(0);
return me();
}
public T string(String value) {
this.currentArray.add(value);
return me();
}
public T bool(boolean value) {
this.currentArray.add(value);
return me();
}
public T number(Number value) {
this.currentArray.add(value);
return me();
}
public JsonObjectInArrayBuilder<T> object(){
JsonObject jo = new JsonObject();
this.currentArray.add(jo);
return new JsonObjectInArrayBuilder<>(jo, me());
}
}
public static class JsonArrayBuilder<T extends CommonObjectBuilder<T>> extends AbstractJsonArrayBuilder<JsonArrayBuilder<T>> {
T parentBuilder;
protected JsonArrayBuilder(JsonArray jsonArray, T parent) {
super(jsonArray);
this.parentBuilder = parent;
}
public JsonArrayInArrayBuilder<JsonArrayBuilder<T>> array() {
JsonArray ja = new JsonArray();
this.currentArray.add(ja);
return new JsonArrayInArrayBuilder<JsonArrayBuilder<T>>(ja, this);
}
public T up() {
return parentBuilder;
}
}
public static class JsonArrayInArrayBuilder<T extends AbstractJsonArrayBuilder<T>> extends AbstractJsonArrayBuilder<JsonArrayInArrayBuilder<T>> {
T parentBuilder;
protected JsonArrayInArrayBuilder(JsonArray jsonArray, T parent) {
super(jsonArray);
this.parentBuilder = parent;
}
public JsonArrayInArrayBuilder<JsonArrayInArrayBuilder<T>> array() {
JsonArray ja = new JsonArray();
this.currentArray.add(ja);
return new JsonArrayInArrayBuilder<JsonArrayInArrayBuilder<T>>(ja, this);
}
public T up() {
return parentBuilder;
}
}
}
}