Extending JSONPath Support
This commit is contained in:
parent
aac6bdf49f
commit
b4478380ec
|
@ -44,7 +44,7 @@
|
|||
<dependency>
|
||||
<groupId>com.jayway.jsonpath</groupId>
|
||||
<artifactId>json-path</artifactId>
|
||||
<version>2.4.0</version>
|
||||
<version>2.7.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
|
|
@ -2,18 +2,23 @@ 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;
|
||||
|
||||
|
@ -44,48 +49,176 @@ public class JSONPathWrapper {
|
|||
|
||||
|
||||
@Getter
|
||||
DocumentContext ctx=null;
|
||||
DocumentContext valueCTX =null;
|
||||
DocumentContext pathsCTX =null;
|
||||
|
||||
|
||||
public JSONPathWrapper(String json) {
|
||||
ctx=JsonPath.using(JSON_PATH_ALWAYS_LIST_CONFIG).parse(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 ctx.read(path);
|
||||
return valueCTX.read(path);
|
||||
}
|
||||
|
||||
public List<String> getMatchingPaths(String path){return pathsCTX.read(path); }
|
||||
|
||||
public <T> List<T> getByPath(String path,Class<T> clazz){
|
||||
List<T> l= ctx.read(path, new TypeRef<List<T>>() {});
|
||||
List<T> l= valueCTX.read(path, new TypeRef<List<T>>() {});
|
||||
l.removeIf(p->p==null);
|
||||
return l;
|
||||
}
|
||||
|
||||
public JSONPathWrapper set(String path, Object toSet){
|
||||
ctx.set(path,toSet);
|
||||
/**
|
||||
* 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)){
|
||||
setElement(p,toSet);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// public static final DocumentContext addElement(DocumentContext ctx,String path) throws JsonPathException{
|
||||
// JsonPath jPath=JsonPath.compile(path);
|
||||
// if(jPath.isDefinite()) {
|
||||
// String parent=path.substring(0,path.lastIndexOf("."));
|
||||
// List<String> found=ctx.read(parent);
|
||||
// if(found==null || found.size()==0 || found.get(0)==null) {
|
||||
// //missing parent, use recursion
|
||||
// addElement(ctx,parent);
|
||||
// }
|
||||
// // found parent, adding element
|
||||
// String element=path.substring(path.lastIndexOf(".")+1);
|
||||
//
|
||||
// Object value=new HashMap<String,String>();
|
||||
// if(element.contains("[")) {
|
||||
// value=new ArrayList<Object>(Collections.singletonList(value));
|
||||
//
|
||||
// element=element.substring(0,element.indexOf("["));
|
||||
// }
|
||||
//
|
||||
// ctx.put(parent, element, value);
|
||||
// return ctx;
|
||||
// }else throw new JsonPathException("Unable to initialize non-definite path : "+path);
|
||||
// }
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ import java.util.List;
|
|||
@Data
|
||||
public class LifecycleInformation {
|
||||
|
||||
public static final String DRAFT_PHASE="DRAFT";
|
||||
|
||||
public static final String PHASE="phase";
|
||||
public static final String LAST_INVOKED_STEP="lastInvokedStep";
|
||||
public static final String LAST_OPERATION_STATUS="lastOperationStatus";
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package org.gcube.application.geoportal.common;
|
||||
|
||||
import com.jayway.jsonpath.Configuration;
|
||||
import com.jayway.jsonpath.Option;
|
||||
import com.jayway.jsonpath.spi.json.JacksonJsonProvider;
|
||||
import com.jayway.jsonpath.spi.json.JsonProvider;
|
||||
import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
|
||||
import com.jayway.jsonpath.spi.mapper.MappingProvider;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class JacksonProvider implements JSONSerializationProvider {
|
||||
|
||||
|
||||
@Override
|
||||
public void setJSONWrapperDefaults() {
|
||||
Configuration.setDefaults(new Configuration.Defaults() {
|
||||
private JsonProvider jacksonProvider = new JacksonJsonProvider();
|
||||
|
||||
private final MappingProvider mappingProvider = new JacksonMappingProvider();
|
||||
@Override
|
||||
public JsonProvider jsonProvider() {
|
||||
return jacksonProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Option> options() {
|
||||
return EnumSet.noneOf(Option.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingProvider mappingProvider() {
|
||||
return mappingProvider;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
package org.gcube.application.geoportal.common.model;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.jayway.jsonpath.Configuration;
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
import com.jayway.jsonpath.Option;
|
||||
import com.jayway.jsonpath.spi.json.JacksonJsonProvider;
|
||||
import com.jayway.jsonpath.spi.json.JsonProvider;
|
||||
import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
|
||||
import com.jayway.jsonpath.spi.mapper.MappingProvider;
|
||||
import org.bson.Document;
|
||||
import org.gcube.application.geoportal.common.JSONSerializationProvider;
|
||||
import org.gcube.application.geoportal.common.model.document.ProfiledDocument;
|
||||
import org.gcube.application.geoportal.common.model.document.lifecycle.TriggeredEvents;
|
||||
import org.gcube.application.geoportal.common.utils.Files;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
|
||||
import static junit.framework.TestCase.*;
|
||||
|
||||
public class JSONPathTests {
|
||||
|
||||
|
||||
static File baseFolder=new File("../test-data/profiledDocuments");
|
||||
|
||||
private JSONPathWrapper profileNavigator() throws IOException {
|
||||
return getNavigator("basicProfile.json");
|
||||
}
|
||||
|
||||
private JSONPathWrapper getNavigator(String filename) throws IOException {
|
||||
return new JSONPathWrapper(Files.readFileAsString(new File(baseFolder, filename).getAbsolutePath(), Charset.defaultCharset()));
|
||||
}
|
||||
|
||||
private ProfiledDocument getDocument(String filename){
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void readElements() throws IOException {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void writeTest() throws IOException {
|
||||
JSONPathWrapper wrapper =getNavigator("emptyProfiledDocument.json");
|
||||
checkSetValue(wrapper,"firstField","value1");
|
||||
|
||||
checkSetValue(wrapper,"$.firstField","value");
|
||||
checkSetValue(wrapper,"$..event","fakeEvent");
|
||||
checkSetValue(wrapper,"$['firstField']","value");
|
||||
|
||||
|
||||
TriggeredEvents event=new TriggeredEvents();
|
||||
event.setEvent("newEvent");
|
||||
checkAddElement(wrapper,"$..triggeredEvents",event);
|
||||
|
||||
|
||||
wrapper=getNavigator("basicProfile.json");
|
||||
checkPutElement(wrapper,"$..[?(@.id == 'SDI-Default-Materializer')].configuration","additional",
|
||||
Collections.singletonMap("myKey","myField"));
|
||||
|
||||
}
|
||||
|
||||
private void checkPutElement(JSONPathWrapper wrapper, String path, String elementName, Object value){
|
||||
wrapper.putElement(path,elementName,value);
|
||||
System.out.println("JSON is "+wrapper.getValueCTX().jsonString());
|
||||
List<Map<?,?>> foundElements= wrapper.getByPath(path,Document.class);
|
||||
|
||||
assertTrue(foundElements!=null);
|
||||
assertTrue(!foundElements.isEmpty());
|
||||
foundElements.forEach(d->{
|
||||
assertTrue(d.containsKey(elementName));
|
||||
assertTrue(d.get(elementName).equals(value));
|
||||
});
|
||||
}
|
||||
|
||||
private void checkSetValue(JSONPathWrapper wrapper, String element, Object value){
|
||||
wrapper.setElement(element,value);
|
||||
System.out.println("JSON is "+wrapper.getValueCTX().jsonString());
|
||||
List<Object> foundElements= wrapper.getByPath(element);
|
||||
|
||||
assertTrue(foundElements!=null);
|
||||
assertTrue(!foundElements.isEmpty());
|
||||
assertTrue(foundElements.get(0).toString().equals(value));
|
||||
|
||||
}
|
||||
|
||||
private void checkAddElement(JSONPathWrapper wrapper, String element, Object value){
|
||||
int sizeBefore = wrapper.getByPath(element,List.class).get(0).size();
|
||||
|
||||
wrapper.addElementToArray(element,value);
|
||||
System.out.println("JSON is "+wrapper.getValueCTX().jsonString());
|
||||
List<Object> foundElements= wrapper.getByPath(element,List.class).get(0);
|
||||
|
||||
assertTrue(foundElements!=null);
|
||||
foundElements.removeIf(f->f==null);
|
||||
assertTrue(!foundElements.isEmpty());
|
||||
assertTrue(foundElements.size()==sizeBefore+1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTokenizer(){
|
||||
assertTrue(JSONPathWrapper.tokenizePath("$.firstField").size()==1);
|
||||
assertTrue(JSONPathWrapper.tokenizePath("$.firstField.child").size()==2);
|
||||
assertTrue(JSONPathWrapper.tokenizePath("$.firstField[3].child").size()==2);
|
||||
assertTrue(JSONPathWrapper.tokenizePath("$.book[?(@.isbn)].child").size()==2);
|
||||
assertTrue(JSONPathWrapper.tokenizePath("$.store.book[*].author").size()==3);
|
||||
|
||||
|
||||
assertTrue(JSONPathWrapper.tokenizePath("$['firstField']").size()==1);
|
||||
assertTrue(JSONPathWrapper.tokenizePath("$['firstField']['child']").size()==2);
|
||||
assertTrue(JSONPathWrapper.tokenizePath("$['firstField'][3]['child']").size()==2);
|
||||
assertTrue(JSONPathWrapper.tokenizePath("$['book'][?(@.isbn)]['child']").size()==2);
|
||||
assertTrue(JSONPathWrapper.tokenizePath("$['store'].['book'][*].['author']").size()==3);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetFieldNameFromPathElement(){
|
||||
assertEquals(JSONPathWrapper.extractFieldNameFromPathElement("$.firstField"),"firstField");
|
||||
assertEquals(JSONPathWrapper.extractFieldNameFromPathElement("firstField"),"firstField");
|
||||
assertEquals(JSONPathWrapper.extractFieldNameFromPathElement("$.firstField[3]"),"firstField");
|
||||
assertEquals(JSONPathWrapper.extractFieldNameFromPathElement("$['book'][?(@.isbn)]"),"book");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefinitePath(){
|
||||
assertTrue(JsonPath.isPathDefinite("$.firstField"));
|
||||
assertTrue(JsonPath.isPathDefinite("$.firstField.child"));
|
||||
assertTrue(JsonPath.isPathDefinite("$.firstField[3].child"));
|
||||
assertFalse(JsonPath.isPathDefinite("$.book[?(@.isbn)].child"));
|
||||
assertFalse(JsonPath.isPathDefinite("$.store.book[*].author"));
|
||||
|
||||
|
||||
assertTrue(JsonPath.isPathDefinite("$['firstField']"));
|
||||
assertTrue(JsonPath.isPathDefinite("$['firstField']['child']"));
|
||||
assertTrue(JsonPath.isPathDefinite("$['firstField'][3]['child']"));
|
||||
assertFalse(JsonPath.isPathDefinite("$['book'][?(@.isbn)]['child']"));
|
||||
assertFalse(JsonPath.isPathDefinite("$['store'].['book'][*].['author']"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss} | %logger{0}:%L - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.gcube.application" level="TRACE"/>
|
||||
<logger name="com.jayway" level="DEBUG"/>
|
||||
<root level="ERROR">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
||||
|
||||
|
||||
</configuration>
|
Loading…
Reference in New Issue