226 lines
7.9 KiB
Java
226 lines
7.9 KiB
Java
package org.gcube.application.geoportal.common.model;
|
|
|
|
|
|
import com.jayway.jsonpath.*;
|
|
import com.mongodb.util.JSON;
|
|
import lombok.Getter;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import org.bson.Document;
|
|
import org.gcube.application.geoportal.common.JSONSerializationProvider;
|
|
import org.reflections.Reflections;
|
|
import org.reflections.util.ConfigurationBuilder;
|
|
import org.reflections.util.FilterBuilder;
|
|
|
|
import java.util.*;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Pattern;
|
|
|
|
@Slf4j
|
|
public class JSONPathWrapper {
|
|
|
|
|
|
public static Configuration JSON_PATH_ALWAYS_LIST_CONFIG=null;
|
|
public static Configuration JSON_PATH_PATHS_CONFIGURATION=null;
|
|
|
|
static {
|
|
Reflections reflections = new Reflections(
|
|
new ConfigurationBuilder()
|
|
.forPackage("org.gcube.application")
|
|
.filterInputsBy(new FilterBuilder().includePackage("org.gcube.application")));
|
|
|
|
reflections.getSubTypesOf(JSONSerializationProvider.class).iterator().forEachRemaining(providerClass->{
|
|
if(!providerClass.isInterface()){
|
|
try {
|
|
log.warn("Loading JSON Provider {} ",providerClass);
|
|
JSONSerializationProvider provider = providerClass.newInstance();
|
|
provider.setJSONWrapperDefaults();
|
|
}catch (Throwable t){
|
|
log.error("Unable to instantiate provider "+providerClass,t);
|
|
}
|
|
}
|
|
|
|
});
|
|
|
|
JSON_PATH_ALWAYS_LIST_CONFIG= Configuration.builder().options(Option.ALWAYS_RETURN_LIST,Option.SUPPRESS_EXCEPTIONS,Option.DEFAULT_PATH_LEAF_TO_NULL).build();
|
|
JSON_PATH_PATHS_CONFIGURATION = Configuration.builder().options(Option.AS_PATH_LIST,Option.SUPPRESS_EXCEPTIONS,Option.DEFAULT_PATH_LEAF_TO_NULL).build();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Getter
|
|
DocumentContext valueCTX =null;
|
|
DocumentContext pathsCTX =null;
|
|
|
|
|
|
public JSONPathWrapper(String json) {
|
|
|
|
valueCTX =JsonPath.using(JSON_PATH_ALWAYS_LIST_CONFIG).parse(json);
|
|
pathsCTX =JsonPath.using(JSON_PATH_PATHS_CONFIGURATION).parse(json);
|
|
}
|
|
|
|
public List<Object> getByPath(String path){
|
|
return getByPath(path,Object.class);
|
|
}
|
|
|
|
public List<String> getMatchingPaths(String path){return pathsCTX.read(path); }
|
|
|
|
public <T> List<T> getByPath(String path,Class<T> clazz){
|
|
List<T> l= valueCTX.read(path, new TypeRef<List<T>>() {});
|
|
l.removeIf(p->p==null);
|
|
return l;
|
|
}
|
|
|
|
/**
|
|
* Changes the value of an existent field at @path
|
|
*
|
|
* @param path
|
|
* @param toSet
|
|
* @return
|
|
*/
|
|
public JSONPathWrapper setElement(String path, Object toSet){
|
|
log.debug("Setting Path {} = {}",path,toSet);
|
|
JsonPath jPath=JsonPath.compile(path);
|
|
if(jPath.isDefinite()){
|
|
valueCTX.set(path,toSet);
|
|
} else{
|
|
log.debug("Path is not definte, evaluating matching paths..");
|
|
for(String p : getMatchingPaths(path)){
|
|
log.debug("Actually setting {} as {} ",p,toSet);
|
|
setElement(p,toSet);
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Creates a new element @elementName == @toSet as child of all matching @path
|
|
*
|
|
* @param path
|
|
* @param elementName
|
|
* @param toSet
|
|
* @return
|
|
*/
|
|
public JSONPathWrapper putElement(String path,String elementName, Object toSet){
|
|
log.debug("Putting {} = {} at Path {}",elementName,toSet,path);
|
|
JsonPath jPath=JsonPath.compile(path);
|
|
if(jPath.isDefinite()){
|
|
valueCTX.put(path,elementName,toSet);
|
|
} else{
|
|
log.debug("Path is not definte, evaluating matching paths..");
|
|
for(String p : getMatchingPaths(path)){
|
|
putElement(p,elementName,toSet);
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Appends a new element @toSet as child of all arrays matching @path
|
|
*
|
|
* @param path
|
|
* @param toAdd
|
|
* @return
|
|
*/
|
|
public JSONPathWrapper addElementToArray(String path, Object toAdd){
|
|
log.debug("Setting Path {} = {}",path,toAdd);
|
|
JsonPath jPath=JsonPath.compile(path);
|
|
if(jPath.isDefinite()){
|
|
valueCTX.add(path,toAdd);
|
|
} else{
|
|
log.debug("Path is not definte, evaluating matching paths..");
|
|
for(String p : getMatchingPaths(path)){
|
|
addElementToArray(p,toAdd);
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
|
|
//***** EXTENSIONS
|
|
|
|
private static Pattern DOTTED_PATH_PATTERN=Pattern.compile("(\\$?\\.\\w*(\\[((\\?\\(.*\\))|(\\d+|\\*))\\])?)(?=(\\.|$)?)");
|
|
private static Pattern BRACKET_PATH_PATTERN=Pattern.compile("(\\$?\\['\\w*'\\](\\[((\\?\\(.*\\))|(\\d+|\\*))\\])?)");
|
|
|
|
|
|
/* private static final DocumentContext addElement(DocumentContext ctx,String path,Object value) throws JsonPathException{
|
|
log.debug("Inserting object at {}",path);
|
|
JsonPath jPath=JsonPath.compile(path);
|
|
if(jPath.isDefinite()) {
|
|
List<String> tokens=tokenizePath(path);
|
|
DocumentContext currentCtx=ctx;
|
|
String parentPath = null;
|
|
for(int i = 0; i<tokens.size();i++){
|
|
String s= tokens.get(i);
|
|
log.trace("Managing {}",s);
|
|
if(parentPath== null) parentPath="$";
|
|
else parentPath +=s;
|
|
|
|
String elementName = extractFieldNameFromPathElement(s);
|
|
|
|
if(i==tokens.size()-1) {
|
|
// IF LEAF SET
|
|
log.trace("{} is leaf, setting as {}", s, value);
|
|
currentCtx.put("$",elementName,value);
|
|
}else {
|
|
// Intermediate element
|
|
|
|
List<String> foundElements = currentCtx.read(s);
|
|
foundElements.removeIf(v->(v==null));
|
|
|
|
if(foundElements.isEmpty()){
|
|
Document newElement=null;
|
|
log.trace("{} not found..");
|
|
// add child
|
|
if (s.matches("")){
|
|
log.trace("{} is array, checking existence",s,value);
|
|
// ARRAY
|
|
// if array exists add elements else create new
|
|
List<?> foundArray= currentCtx.read(elementName,List.class);
|
|
throw new RuntimeException("Implement array support");
|
|
} else {
|
|
// Map
|
|
newElement = new Document();
|
|
}
|
|
currentCtx.put("$",elementName,newElement);
|
|
log.trace("Set {}, json is {}",s,currentCtx.jsonString());
|
|
currentCtx= JsonPath.parse(newElement.toJson());
|
|
}else currentCtx = JsonPath.parse(currentCtx.read(s,Document.class));
|
|
}
|
|
} return ctx;
|
|
}else throw new JsonPathException("Unable to initialize non-definite path : "+path);
|
|
}
|
|
|
|
*/
|
|
static List<String> tokenizePath(String path){
|
|
List<String> toReturn=null;
|
|
log.debug("Tokenizing JSON Path {} ",path);
|
|
// if path is $.element.child
|
|
if(path.matches("(\\$\\.)?\\w.*"))
|
|
toReturn= getByRegex(path, DOTTED_PATH_PATTERN);
|
|
else if (path.matches("(\\$)?\\[\\'.*"))
|
|
toReturn = getByRegex(path, BRACKET_PATH_PATTERN);
|
|
|
|
log.debug("Path {} as tokens {}",path,toReturn);
|
|
return toReturn;
|
|
}
|
|
|
|
private static List<String> getByRegex(String s,Pattern p){
|
|
ArrayList<String> toReturn = new ArrayList<>();
|
|
Matcher m = p.matcher(s);
|
|
log.trace("Groups from {} with {} : ",s,p.pattern());
|
|
while(m.find()) {
|
|
String found=m.group();
|
|
toReturn.add(found);
|
|
}
|
|
return toReturn;
|
|
}
|
|
|
|
static String extractFieldNameFromPathElement(String p){
|
|
Matcher m=Pattern.compile("\\$?\\.?(\\[')?([a-zA-Z_]+)").matcher(p);
|
|
m.find();
|
|
return m.group(2);
|
|
}
|
|
|
|
}
|