116 changed files with 65579 additions and 4505 deletions
@ -0,0 +1,120 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
<parent> |
||||
<groupId>com.jayway.jsonpath</groupId> |
||||
<artifactId>json-path-parent</artifactId> |
||||
<version>0.9.2-SNAPSHOT</version> |
||||
</parent> |
||||
|
||||
<groupId>com.jayway.jsonpath</groupId> |
||||
<artifactId>json-path-web-test</artifactId> |
||||
<version>0.9.2-SNAPSHOT</version> |
||||
|
||||
<properties> |
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
||||
<gatling-jsonpath.version>0.4.0</gatling-jsonpath.version> |
||||
<boon.version>0.13</boon.version> |
||||
</properties> |
||||
|
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>org.eclipse.jetty.aggregate</groupId> |
||||
<artifactId>jetty-all</artifactId> |
||||
<version>9.2.0.v20140526</version> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.glassfish.jersey.containers</groupId> |
||||
<artifactId>jersey-container-servlet</artifactId> |
||||
<version>2.9.1</version> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.glassfish.jersey.ext</groupId> |
||||
<artifactId>jersey-mvc-mustache</artifactId> |
||||
<version>2.9.1</version> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.glassfish.jersey.media</groupId> |
||||
<artifactId>jersey-media-json-jackson</artifactId> |
||||
<version>2.9.1</version> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.slf4j</groupId> |
||||
<artifactId>slf4j-simple</artifactId> |
||||
<scope>runtime</scope> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.apache.commons</groupId> |
||||
<artifactId>commons-io</artifactId> |
||||
<version>1.3.2</version> |
||||
</dependency> |
||||
|
||||
|
||||
<dependency> |
||||
<groupId>com.jayway.jsonpath</groupId> |
||||
<artifactId>json-path</artifactId> |
||||
<version>0.9.2-SNAPSHOT</version> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>io.fastjson</groupId> |
||||
<artifactId>boon</artifactId> |
||||
<version>${boon.version}</version> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.nebhale.jsonpath</groupId> |
||||
<artifactId>jsonpath</artifactId> |
||||
<version>1.2</version> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.codehaus.jackson</groupId> |
||||
<artifactId>jackson-mapper-asl</artifactId> |
||||
<version>${jackson.version}</version> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>io.gatling</groupId> |
||||
<artifactId>jsonpath_2.10</artifactId> |
||||
<version>${gatling-jsonpath.version}</version> |
||||
</dependency> |
||||
|
||||
|
||||
</dependencies> |
||||
|
||||
<build> |
||||
<plugins> |
||||
<plugin> |
||||
<groupId>org.apache.maven.plugins</groupId> |
||||
<artifactId>maven-jar-plugin</artifactId> |
||||
<version>2.4</version> |
||||
<configuration> |
||||
<archive> |
||||
<manifest> |
||||
<mainClass>com.jayway.oauth2.example.boot.Main</mainClass> |
||||
</manifest> |
||||
</archive> |
||||
</configuration> |
||||
</plugin> |
||||
<plugin> |
||||
<groupId>com.jolira</groupId> |
||||
<artifactId>onejar-maven-plugin</artifactId> |
||||
<version>1.4.4</version> |
||||
<executions> |
||||
<execution> |
||||
<configuration> |
||||
<onejarVersion>0.97</onejarVersion> |
||||
<attachToBuild>true</attachToBuild> |
||||
<classifier>onejar</classifier> |
||||
</configuration> |
||||
<goals> |
||||
<goal>one-jar</goal> |
||||
</goals> |
||||
</execution> |
||||
</executions> |
||||
</plugin> |
||||
</plugins> |
||||
</build> |
||||
</project> |
@ -0,0 +1,131 @@
|
||||
package com.jayway.jsonpath.web.bench; |
||||
|
||||
import com.jayway.jsonpath.JsonPath; |
||||
import com.jayway.jsonpath.internal.spi.json.JacksonProvider; |
||||
import com.jayway.jsonpath.spi.json.JsonProvider; |
||||
import com.jayway.jsonpath.spi.json.JsonProviderFactory; |
||||
import io.gatling.jsonpath.JsonPath$; |
||||
import org.boon.json.JsonParser; |
||||
import org.boon.json.ObjectMapper; |
||||
import org.boon.json.implementation.JsonParserCharArray; |
||||
import org.boon.json.implementation.ObjectMapperImpl; |
||||
import scala.collection.Iterator; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import static java.util.Arrays.asList; |
||||
|
||||
public class Bench { |
||||
|
||||
protected final String json; |
||||
protected final String path; |
||||
private final boolean value; |
||||
|
||||
public Bench(String json, String path, boolean value) { |
||||
this.json = json; |
||||
this.path = path; |
||||
this.value = value; |
||||
} |
||||
|
||||
public Result runJayway() { |
||||
String result = null; |
||||
String error = null; |
||||
long time; |
||||
Object res = null; |
||||
long now = System.currentTimeMillis(); |
||||
|
||||
try { |
||||
if(value) { |
||||
res = JsonPath.parse(json).read(path); |
||||
} else { |
||||
res = JsonPath.parse(json).readPathList(path); |
||||
} |
||||
|
||||
} catch (Exception e){ |
||||
error = getError(e); |
||||
} finally { |
||||
time = System.currentTimeMillis() - now; |
||||
|
||||
if(res instanceof String) { |
||||
result = "\"" + res + "\""; |
||||
} else if(res instanceof Number) { |
||||
result = res.toString(); |
||||
} else if(res instanceof Boolean){ |
||||
result = res.toString(); |
||||
} else { |
||||
result = res != null ? JsonProviderFactory.createProvider().toJson(res) : null; |
||||
} |
||||
return new Result("jayway", time, result, error); |
||||
} |
||||
} |
||||
|
||||
public Result runBoon() { |
||||
String result = null; |
||||
String error = null; |
||||
long time; |
||||
|
||||
Iterator<Object> query = null; |
||||
long now = System.currentTimeMillis(); |
||||
try { |
||||
if(!value){ |
||||
throw new UnsupportedOperationException("Not supported!"); |
||||
} |
||||
io.gatling.jsonpath.JsonPath jsonPath = JsonPath$.MODULE$.compile(path).right().get(); |
||||
|
||||
JsonParser jsonParser = new JsonParserCharArray(); |
||||
Object jsonModel = jsonParser.parse(json); |
||||
query = jsonPath.query(jsonModel); |
||||
|
||||
} catch (Exception e){ |
||||
error = getError(e); |
||||
} finally { |
||||
time = System.currentTimeMillis() - now; |
||||
|
||||
if(query != null) { |
||||
List<Object> res = new ArrayList<Object>(); |
||||
while (query.hasNext()) { |
||||
res.add(query.next()); |
||||
} |
||||
ObjectMapper mapper = new ObjectMapperImpl(); |
||||
result = mapper.toJson(res); |
||||
} |
||||
return new Result("boon", time, result, error); |
||||
} |
||||
} |
||||
|
||||
public Result runNebhale() { |
||||
String result = null; |
||||
String error = null; |
||||
long time; |
||||
Object res = null; |
||||
JacksonProvider jacksonProvider = new JacksonProvider(); |
||||
|
||||
long now = System.currentTimeMillis(); |
||||
try { |
||||
if(!value){ |
||||
throw new UnsupportedOperationException("Not supported!"); |
||||
} |
||||
com.nebhale.jsonpath.JsonPath compiled = com.nebhale.jsonpath.JsonPath.compile(path); |
||||
res = compiled.read(json, Object.class); |
||||
} catch (Exception e){ |
||||
error = getError(e); |
||||
} finally { |
||||
time = System.currentTimeMillis() - now; |
||||
result = res!=null? jacksonProvider.toJson(res):null; |
||||
return new Result("nebhale", time, result, error); |
||||
} |
||||
} |
||||
|
||||
public List<Result> runAll(){ |
||||
return asList(runJayway(), runBoon(), runNebhale()); |
||||
} |
||||
|
||||
private String getError(Exception e){ |
||||
String ex = e.getMessage(); |
||||
if(ex == null || ex.trim().isEmpty()){ |
||||
ex = "Undefined error"; |
||||
} |
||||
return ex; |
||||
} |
||||
} |
@ -0,0 +1,22 @@
|
||||
package com.jayway.jsonpath.web.bench; |
||||
|
||||
import com.jayway.jsonpath.internal.JsonFormatter; |
||||
|
||||
import java.util.List; |
||||
|
||||
public class Result { |
||||
|
||||
public final String provider; |
||||
public final String active; |
||||
public final long time; |
||||
public final String result; |
||||
public final String error; |
||||
|
||||
public Result(String provider, long time, String result, String error) { |
||||
this.provider = provider; |
||||
this.active = provider == "jayway" ? "active" : ""; |
||||
this.time = time; |
||||
this.result = result != null ? JsonFormatter.prettyPrint(result) : result; |
||||
this.error = error; |
||||
} |
||||
} |
@ -0,0 +1,58 @@
|
||||
package com.jayway.jsonpath.web.boot; |
||||
|
||||
import static org.eclipse.jetty.servlet.ServletContextHandler.NO_SESSIONS; |
||||
|
||||
import java.io.IOException; |
||||
|
||||
import com.jayway.jsonpath.web.resource.IndexResource; |
||||
import com.jayway.jsonpath.web.resource.StaticResource; |
||||
import org.eclipse.jetty.server.Connector; |
||||
import org.eclipse.jetty.server.Server; |
||||
import org.eclipse.jetty.server.ServerConnector; |
||||
import org.eclipse.jetty.servlet.ServletContextHandler; |
||||
import org.eclipse.jetty.servlet.ServletHolder; |
||||
import org.glassfish.jersey.jackson.JacksonFeature; |
||||
import org.glassfish.jersey.server.ResourceConfig; |
||||
import org.glassfish.jersey.server.mvc.mustache.MustacheMvcFeature; |
||||
import org.glassfish.jersey.servlet.ServletContainer; |
||||
|
||||
|
||||
|
||||
public class Main { |
||||
|
||||
public static void main(String[] args) throws Exception { |
||||
Server s = new Server(); |
||||
|
||||
s.setConnectors(new Connector[] { createConnector(s) }); |
||||
|
||||
ServletContextHandler context = new ServletContextHandler(NO_SESSIONS); |
||||
context.setContextPath("/"); |
||||
ServletHolder servletHolder = new ServletHolder(createJerseyServlet()); |
||||
servletHolder.setInitOrder(1); |
||||
context.addServlet(servletHolder, "/*"); |
||||
s.setHandler(context); |
||||
|
||||
s.start(); |
||||
s.join(); |
||||
} |
||||
|
||||
private static ServerConnector createConnector(Server s){ |
||||
ServerConnector connector = new ServerConnector(s); |
||||
connector.setHost("localhost"); |
||||
connector.setPort(8080); |
||||
return connector; |
||||
} |
||||
|
||||
private static ServletContainer createJerseyServlet() throws IOException { |
||||
ResourceConfig resourceConfig = new ResourceConfig(); |
||||
resourceConfig.property(MustacheMvcFeature.TEMPLATE_BASE_PATH, "templates"); |
||||
|
||||
resourceConfig.register(MustacheMvcFeature.class); |
||||
resourceConfig.register(JacksonFeature.class); |
||||
|
||||
resourceConfig.register(new IndexResource()); |
||||
resourceConfig.register(new StaticResource()); |
||||
|
||||
return new ServletContainer(resourceConfig); |
||||
} |
||||
} |
@ -0,0 +1,96 @@
|
||||
package com.jayway.jsonpath.web.resource; |
||||
|
||||
import com.jayway.jsonpath.web.bench.Bench; |
||||
import com.jayway.jsonpath.web.bench.Result; |
||||
import org.apache.commons.io.IOUtils; |
||||
import org.glassfish.jersey.server.mvc.Viewable; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
import javax.ws.rs.Consumes; |
||||
import javax.ws.rs.DefaultValue; |
||||
import javax.ws.rs.FormParam; |
||||
import javax.ws.rs.GET; |
||||
import javax.ws.rs.POST; |
||||
import javax.ws.rs.Path; |
||||
import javax.ws.rs.Produces; |
||||
import javax.ws.rs.QueryParam; |
||||
import javax.ws.rs.core.MediaType; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import static java.util.Arrays.asList; |
||||
|
||||
@Path("") |
||||
@Produces(MediaType.TEXT_HTML) |
||||
public class IndexResource { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(IndexResource.class); |
||||
|
||||
public final static Map<String, String> TEMPLATES = loadTemplates(); |
||||
|
||||
@GET |
||||
public Viewable get(@QueryParam("template") @DefaultValue("goessner") String template){ |
||||
return createView(TEMPLATES.get(template), "$.store.book[0].title", true, template, null); |
||||
} |
||||
|
||||
@POST |
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED) |
||||
public Viewable post(@FormParam("json") String json, |
||||
@FormParam("path") String path, |
||||
@FormParam("type") String type, |
||||
@FormParam("template") String template){ |
||||
|
||||
boolean value = "VALUE".equalsIgnoreCase(type); |
||||
|
||||
return createView(json, path, value, template, new Bench(json, path, value).runAll()); |
||||
} |
||||
|
||||
private Viewable createView(String json, String path, boolean value, String selectedTemplate, List<Result> results){ |
||||
Map<String, Object> res = new HashMap<String, Object>(); |
||||
res.put("results", results); |
||||
res.put("json", json); |
||||
res.put("path", path); |
||||
res.put("value-checked", value?"checked":""); |
||||
res.put("path-checked", (!value)?"checked":""); |
||||
res.put("templates", asList(new Template("goessner", "goessner", selectedTemplate), new Template("twitter" , "twitter", selectedTemplate), new Template("webapp", "webapp", selectedTemplate), new Template("20k", "20k", selectedTemplate))); |
||||
|
||||
return new Viewable("/index", res); |
||||
} |
||||
|
||||
|
||||
|
||||
private static Map<String, String> loadTemplates(){ |
||||
try { |
||||
String goessner = IOUtils.toString(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/goessner.json"), "UTF-8"); |
||||
String twitter = IOUtils.toString(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/twitter.json")); |
||||
String webapp = IOUtils.toString(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/webxml.json")); |
||||
String twentyK = IOUtils.toString(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/20k.json")); |
||||
|
||||
Map<String, String> templates = new HashMap<String, String>(); |
||||
templates.put("goessner", goessner); |
||||
templates.put("twitter", twitter); |
||||
templates.put("webapp", webapp); |
||||
templates.put("20k", twentyK); |
||||
|
||||
return templates; |
||||
} catch (Exception e){ |
||||
throw new RuntimeException(e); |
||||
} |
||||
} |
||||
|
||||
private static class Template { |
||||
public final String value; |
||||
public final String name; |
||||
public final String selected; |
||||
|
||||
private Template(String value, String name, String selectedValue) { |
||||
this.value = value; |
||||
this.name = name; |
||||
this.selected = value.equalsIgnoreCase(selectedValue)?"selected":""; |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,34 @@
|
||||
package com.jayway.jsonpath.web.resource; |
||||
|
||||
import javax.ws.rs.GET; |
||||
import javax.ws.rs.Path; |
||||
import javax.ws.rs.Produces; |
||||
import javax.ws.rs.core.Context; |
||||
import javax.ws.rs.core.UriInfo; |
||||
|
||||
import java.io.InputStream; |
||||
|
||||
@Path("/static/") |
||||
public class StaticResource { |
||||
|
||||
@GET |
||||
@Path("fonts/{resource}") |
||||
//@Produces("text/javascript")
|
||||
public InputStream getFonts(@Context UriInfo uriInfo){ |
||||
return Thread.currentThread().getContextClassLoader().getResourceAsStream("fonts/" + uriInfo.getPathParameters().getFirst("resource")); |
||||
} |
||||
|
||||
@GET |
||||
@Path("js/{resource}") |
||||
@Produces("text/javascript") |
||||
public InputStream getJs(@Context UriInfo uriInfo){ |
||||
return Thread.currentThread().getContextClassLoader().getResourceAsStream("js/" + uriInfo.getPathParameters().getFirst("resource")); |
||||
} |
||||
|
||||
@GET |
||||
@Path("css/{resource}") |
||||
@Produces("text/css") |
||||
public InputStream getCss(@Context UriInfo uriInfo){ |
||||
return Thread.currentThread().getContextClassLoader().getResourceAsStream("css/" + uriInfo.getPathParameters().getFirst("resource")); |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.
After Width: | Height: | Size: 62 KiB |
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,87 @@
|
||||
/* JSONPath 0.8.0 - XPath for JSON |
||||
* |
||||
* Copyright (c) 2007 Stefan Goessner (goessner.net) |
||||
* Licensed under the MIT (MIT-LICENSE.txt) licence. |
||||
*/ |
||||
function jsonPath(obj, expr, arg) { |
||||
var P = { |
||||
resultType: arg && arg.resultType || "VALUE", |
||||
result: [], |
||||
normalize: function(expr) { |
||||
var subx = []; |
||||
return expr.replace(/[\['](\??\(.*?\))[\]']/g, function($0,$1){return "[#"+(subx.push($1)-1)+"]";}) |
||||
.replace(/'?\.'?|\['?/g, ";") |
||||
.replace(/;;;|;;/g, ";..;") |
||||
.replace(/;$|'?\]|'$/g, "") |
||||
.replace(/#([0-9]+)/g, function($0,$1){return subx[$1];}); |
||||
}, |
||||
asPath: function(path) { |
||||
var x = path.split(";"), p = "$"; |
||||
for (var i=1,n=x.length; i<n; i++) |
||||
p += /^[0-9*]+$/.test(x[i]) ? ("["+x[i]+"]") : ("['"+x[i]+"']"); |
||||
return p; |
||||
}, |
||||
store: function(p, v) { |
||||
if (p) P.result[P.result.length] = P.resultType == "PATH" ? P.asPath(p) : v; |
||||
return !!p; |
||||
}, |
||||
trace: function(expr, val, path) { |
||||
if (expr) { |
||||
var x = expr.split(";"), loc = x.shift(); |
||||
x = x.join(";"); |
||||
if (val && val.hasOwnProperty(loc)) |
||||
P.trace(x, val[loc], path + ";" + loc); |
||||
else if (loc === "*") |
||||
P.walk(loc, x, val, path, function(m,l,x,v,p) { P.trace(m+";"+x,v,p); }); |
||||
else if (loc === "..") { |
||||
P.trace(x, val, path); |
||||
P.walk(loc, x, val, path, function(m,l,x,v,p) { typeof v[m] === "object" && P.trace("..;"+x,v[m],p+";"+m); }); |
||||
} |
||||
else if (/,/.test(loc)) { // [name1,name2,...]
|
||||
for (var s=loc.split(/'?,'?/),i=0,n=s.length; i<n; i++) |
||||
P.trace(s[i]+";"+x, val, path); |
||||
} |
||||
else if (/^\(.*?\)$/.test(loc)) // [(expr)]
|
||||
P.trace(P.eval(loc, val, path.substr(path.lastIndexOf(";")+1))+";"+x, val, path); |
||||
else if (/^\?\(.*?\)$/.test(loc)) // [?(expr)]
|
||||
P.walk(loc, x, val, path, function(m,l,x,v,p) { if (P.eval(l.replace(/^\?\((.*?)\)$/,"$1"),v[m],m)) P.trace(m+";"+x,v,p); }); |
||||
else if (/^(-?[0-9]*):(-?[0-9]*):?([0-9]*)$/.test(loc)) // [start:end:step] phyton slice syntax
|
||||
P.slice(loc, x, val, path); |
||||
} |
||||
else |
||||
P.store(path, val); |
||||
}, |
||||
walk: function(loc, expr, val, path, f) { |
||||
if (val instanceof Array) { |
||||
for (var i=0,n=val.length; i<n; i++) |
||||
if (i in val) |
||||
f(i,loc,expr,val,path); |
||||
} |
||||
else if (typeof val === "object") { |
||||
for (var m in val) |
||||
if (val.hasOwnProperty(m)) |
||||
f(m,loc,expr,val,path); |
||||
} |
||||
}, |
||||
slice: function(loc, expr, val, path) { |
||||
if (val instanceof Array) { |
||||
var len=val.length, start=0, end=len, step=1; |
||||
loc.replace(/^(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)$/g, function($0,$1,$2,$3){start=parseInt($1||start);end=parseInt($2||end);step=parseInt($3||step);}); |
||||
start = (start < 0) ? Math.max(0,start+len) : Math.min(len,start); |
||||
end = (end < 0) ? Math.max(0,end+len) : Math.min(len,end); |
||||
for (var i=start; i<end; i+=step) |
||||
P.trace(i+";"+expr, val, path); |
||||
} |
||||
}, |
||||
eval: function(x, _v, _vname) { |
||||
try { return $ && _v && eval(x.replace(/@/g, "_v")); } |
||||
catch(e) { throw new SyntaxError("jsonPath: " + e.message + ": " + x.replace(/@/g, "_v").replace(/\^/g, "_a")); } |
||||
} |
||||
}; |
||||
|
||||
var $ = obj; |
||||
if (expr && obj && (P.resultType == "VALUE" || P.resultType == "PATH")) { |
||||
P.trace(P.normalize(expr).replace(/^\$;/,""), obj, "$"); |
||||
return P.result.length ? P.result : false; |
||||
} |
||||
}
|
@ -0,0 +1,926 @@
|
||||
[ |
||||
{ |
||||
"id": 0, |
||||
"guid": "d7c8d5e2-cfdc-43b9-9bd4-15e8fd344157", |
||||
"isActive": true, |
||||
"balance": "$3,772.00", |
||||
"picture": "http://placehold.it/32x32", |
||||
"age": 35, |
||||
"name": "Snow Rodriguez", |
||||
"gender": "male", |
||||
"company": "Applideck", |
||||
"email": "snowrodriguez@applideck.com", |
||||
"phone": "+1 (867) 551-3944", |
||||
"address": "548 Maple Avenue, Russellville, Pennsylvania, 2467", |
||||
"about": "Ex aute adipisicing incididunt labore est magna aliquip non consequat fugiat nostrud ex. Occaecat commodo irure ut mollit veniam fugiat esse esse laboris velit officia. Duis cillum aute ipsum sunt elit irure laborum nostrud. Dolor aliquip reprehenderit voluptate enim cupidatat nisi cillum enim quis aute fugiat id consequat.\r\n", |
||||
"registered": "1992-04-24T19:03:08 -02:00", |
||||
"latitude": -69.347636, |
||||
"longitude": 92.197293, |
||||
"tags": [ |
||||
"enim", |
||||
"culpa", |
||||
"proident", |
||||
"duis", |
||||
"aute", |
||||
"tempor", |
||||
"anim" |
||||
], |
||||
"friends": [ |
||||
{ |
||||
"id": 0, |
||||
"name": "Garrison Tillman" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"name": "Shawn Holman" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"name": "Amy Guerra" |
||||
} |
||||
], |
||||
"randomArrayItem": "lemon" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"guid": "2b7e2b0a-288f-404e-9fd0-ad2064cc49f4", |
||||
"isActive": true, |
||||
"balance": "$3,046.00", |
||||
"picture": "http://placehold.it/32x32", |
||||
"age": 24, |
||||
"name": "Phyllis Calhoun", |
||||
"gender": "female", |
||||
"company": "Olucore", |
||||
"email": "phylliscalhoun@olucore.com", |
||||
"phone": "+1 (890) 443-2093", |
||||
"address": "355 Brighton Avenue, Shrewsbury, Washington, 8719", |
||||
"about": "Excepteur magna culpa do exercitation anim cillum ad proident nostrud ex. Voluptate sint pariatur nisi magna et cillum Lorem Lorem in deserunt eu. Proident dolor laborum nisi anim. Veniam aliqua nulla minim adipisicing magna exercitation veniam sint ea minim nulla.\r\n", |
||||
"registered": "1996-12-05T15:20:59 -01:00", |
||||
"latitude": -19.65876, |
||||
"longitude": 168.158924, |
||||
"tags": [ |
||||
"ad", |
||||
"exercitation", |
||||
"laboris", |
||||
"id", |
||||
"incididunt", |
||||
"nisi", |
||||
"non" |
||||
], |
||||
"friends": [ |
||||
{ |
||||
"id": 0, |
||||
"name": "Serena Collins" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"name": "Carolina Anthony" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"name": "Kendra Avery" |
||||
} |
||||
], |
||||
"randomArrayItem": "lemon" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"guid": "523d89ac-63aa-4fac-9352-07cbd8f21a6a", |
||||
"isActive": true, |
||||
"balance": "$1,242.00", |
||||
"picture": "http://placehold.it/32x32", |
||||
"age": 38, |
||||
"name": "Lindsay Stuart", |
||||
"gender": "female", |
||||
"company": "Papricut", |
||||
"email": "lindsaystuart@papricut.com", |
||||
"phone": "+1 (859) 589-3940", |
||||
"address": "688 Wyckoff Street, Carrizo, California, 6999", |
||||
"about": "Do exercitation labore in excepteur laborum eiusmod enim officia proident nulla veniam. Do Lorem dolor eiusmod proident qui in consequat aliqua. Lorem nulla id consequat ea enim ad id exercitation dolor ad dolore.\r\n", |
||||
"registered": "1997-01-09T23:08:18 -01:00", |
||||
"latitude": 87.910568, |
||||
"longitude": 163.037238, |
||||
"tags": [ |
||||
"qui", |
||||
"mollit", |
||||
"aute", |
||||
"et", |
||||
"veniam", |
||||
"elit", |
||||
"labore" |
||||
], |
||||
"friends": [ |
||||
{ |
||||
"id": 0, |
||||
"name": "Bullock Hutchinson" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"name": "Guthrie Clark" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"name": "Elena Delaney" |
||||
} |
||||
], |
||||
"randomArrayItem": "cherry" |
||||
}, |
||||
{ |
||||
"id": 3, |
||||
"guid": "bc7a2767-94e5-4ebc-8f9d-ead42c3e0107", |
||||
"isActive": true, |
||||
"balance": "$2,533.00", |
||||
"picture": "http://placehold.it/32x32", |
||||
"age": 33, |
||||
"name": "Mcmahon Adams", |
||||
"gender": "male", |
||||
"company": "Balooba", |
||||
"email": "mcmahonadams@balooba.com", |
||||
"phone": "+1 (984) 475-3671", |
||||
"address": "880 Pilling Street, Omar, Minnesota, 8563", |
||||
"about": "Excepteur incididunt commodo veniam enim esse magna. Exercitation dolor cupidatat consequat irure. Ex duis dolor non nostrud duis aliqua enim labore aliquip. Ad qui reprehenderit aute aliquip duis magna aliquip nisi nulla. Cillum ad sint reprehenderit officia id nostrud cillum excepteur cillum ea labore ullamco esse. Laboris sint ut id incididunt Lorem non reprehenderit adipisicing. Id sint laboris deserunt ea.\r\n", |
||||
"registered": "2005-03-01T02:26:24 -01:00", |
||||
"latitude": 6.359315, |
||||
"longitude": 33.053339, |
||||
"tags": [ |
||||
"Lorem", |
||||
"ex", |
||||
"dolore", |
||||
"pariatur", |
||||
"officia", |
||||
"ipsum", |
||||
"dolore" |
||||
], |
||||
"friends": [ |
||||
{ |
||||
"id": 0, |
||||
"name": "Mollie Wilcox" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"name": "Alta Hall" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"name": "Paulette Pollard" |
||||
} |
||||
], |
||||
"randomArrayItem": "lemon" |
||||
}, |
||||
{ |
||||
"id": 4, |
||||
"guid": "9bbe7981-9e4c-4140-a7f4-be8fcae8ae18", |
||||
"isActive": true, |
||||
"balance": "$2,731.00", |
||||
"picture": "http://placehold.it/32x32", |
||||
"age": 29, |
||||
"name": "Nanette Butler", |
||||
"gender": "female", |
||||
"company": "Nikuda", |
||||
"email": "nanettebutler@nikuda.com", |
||||
"phone": "+1 (989) 498-2116", |
||||
"address": "637 Colby Court, Whitewater, Vermont, 1992", |
||||
"about": "Magna Lorem sit sit duis. Aliqua eiusmod dolor enim est duis nulla in eu deserunt commodo. Sit laboris deserunt sint duis reprehenderit in sunt excepteur proident. Elit ad aliquip deserunt in ad occaecat id elit non commodo adipisicing ullamco magna.\r\n", |
||||
"registered": "1995-04-25T05:22:25 -02:00", |
||||
"latitude": -45.069546, |
||||
"longitude": 83.01566, |
||||
"tags": [ |
||||
"est", |
||||
"in", |
||||
"nulla", |
||||
"ea", |
||||
"dolore", |
||||
"aliqua", |
||||
"deserunt" |
||||
], |
||||
"friends": [ |
||||
{ |
||||
"id": 0, |
||||
"name": "Noble Rodgers" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"name": "Haley Mayer" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"name": "Hudson Gentry" |
||||
} |
||||
], |
||||
"randomArrayItem": "lemon" |
||||
}, |
||||
{ |
||||
"id": 5, |
||||
"guid": "f0449f33-5c39-4a94-a114-e96d466cc9a8", |
||||
"isActive": true, |
||||
"balance": "$1,769.00", |
||||
"picture": "http://placehold.it/32x32", |
||||
"age": 26, |
||||
"name": "Miranda Thomas", |
||||
"gender": "male", |
||||
"company": "Softmicro", |
||||
"email": "mirandathomas@softmicro.com", |
||||
"phone": "+1 (870) 591-2118", |
||||
"address": "503 Linden Street, Jackpot, Rhode Island, 419", |
||||
"about": "Consequat tempor esse pariatur minim magna ut ut dolor officia anim. Mollit mollit tempor do dolor ad aute proident commodo sit ea minim. Eu et laborum est in. Non exercitation est deserunt do mollit tempor duis. Nisi eu nulla ea cillum minim officia et duis quis sint. Consequat velit cupidatat Lorem sunt do do quis et ullamco. Elit eiusmod consectetur ullamco ea amet sint aute quis ipsum.\r\n", |
||||
"registered": "2010-10-07T07:31:46 -02:00", |
||||
"latitude": -28.329413, |
||||
"longitude": -17.80299, |
||||
"tags": [ |
||||
"fugiat", |
||||
"reprehenderit", |
||||
"adipisicing", |
||||
"culpa", |
||||
"quis", |
||||
"pariatur", |
||||
"deserunt" |
||||
], |
||||
"friends": [ |
||||
{ |
||||
"id": 0, |
||||
"name": "Brewer Norton" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"name": "Ramona Jacobs" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"name": "Russo Hewitt" |
||||
} |
||||
], |
||||
"randomArrayItem": "apple" |
||||
}, |
||||
{ |
||||
"id": 6, |
||||
"guid": "18fb0572-e2b8-4f47-bd71-dc9349396227", |
||||
"isActive": true, |
||||
"balance": "$3,081.00", |
||||
"picture": "http://placehold.it/32x32", |
||||
"age": 30, |
||||
"name": "Ada Potter", |
||||
"gender": "female", |
||||
"company": "Melbacor", |
||||
"email": "adapotter@melbacor.com", |
||||
"phone": "+1 (913) 474-2861", |
||||
"address": "947 Sumner Place, Haena, North Dakota, 685", |
||||
"about": "Nostrud amet id id qui. Reprehenderit officia duis ad aute ipsum eiusmod excepteur Lorem voluptate. Cillum laborum cillum aliquip duis reprehenderit amet sint non nulla in culpa irure veniam aliqua.\r\n", |
||||
"registered": "2008-09-26T03:40:28 -02:00", |
||||
"latitude": -11.153539, |
||||
"longitude": 115.382908, |
||||
"tags": [ |
||||
"laboris", |
||||
"proident", |
||||
"dolore", |
||||
"sunt", |
||||
"ut", |
||||
"irure", |
||||
"dolor" |
||||
], |
||||
"friends": [ |
||||
{ |
||||
"id": 0, |
||||
"name": "Bethany Carroll" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"name": "Moody Andrews" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"name": "Trujillo Hester" |
||||
} |
||||
], |
||||
"randomArrayItem": "cherry" |
||||
}, |
||||
{ |
||||
"id": 7, |
||||
"guid": "69cc1cdc-2953-4a00-84df-bed00120ab12", |
||||
"isActive": true, |
||||
"balance": "$2,520.00", |
||||
"picture": "http://placehold.it/32x32", |
||||
"age": 36, |
||||
"name": "Levine Deleon", |
||||
"gender": "male", |
||||
"company": "Dragbot", |
||||
"email": "levinedeleon@dragbot.com", |
||||
"phone": "+1 (979) 575-3162", |
||||
"address": "902 Elmwood Avenue, Caberfae, Delaware, 4651", |
||||
"about": "Laboris nulla velit dolor ex duis pariatur aute est. Incididunt incididunt laborum non culpa. Consequat elit ipsum et sint nisi dolore dolore nisi ut. Quis nostrud ex ad duis. Enim aliquip pariatur deserunt amet est ullamco in. Dolor sunt deserunt id deserunt labore reprehenderit enim ut in consectetur laboris incididunt in nisi. Ipsum sint in ipsum minim ipsum proident commodo excepteur cillum.\r\n", |
||||
"registered": "2000-08-15T05:06:23 -02:00", |
||||
"latitude": -48.334821, |
||||
"longitude": 66.328798, |
||||
"tags": [ |
||||
"nostrud", |
||||
"labore", |
||||
"ipsum", |
||||
"reprehenderit", |
||||
"mollit", |
||||
"consequat", |
||||
"adipisicing" |
||||
], |
||||
"friends": [ |
||||
{ |
||||
"id": 0, |
||||
"name": "Jolene Vang" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"name": "Diana Frost" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"name": "Bobbi Franklin" |
||||
} |
||||
], |
||||
"randomArrayItem": "apple" |
||||
}, |
||||
{ |
||||
"id": 8, |
||||
"guid": "3a6bf80f-273e-40e9-b5b4-d6d7e07e76c6", |
||||
"isActive": false, |
||||
"balance": "$1,694.00", |
||||
"picture": "http://placehold.it/32x32", |
||||
"age": 32, |
||||
"name": "Sophia Duran", |
||||
"gender": "female", |
||||
"company": "Magmina", |
||||
"email": "sophiaduran@magmina.com", |
||||
"phone": "+1 (843) 415-3460", |
||||
"address": "133 Norfolk Street, Worcester, Virginia, 3631", |
||||
"about": "Lorem occaecat officia proident consectetur sint anim id sit anim. Deserunt veniam aute deserunt proident. Ut cillum aliquip deserunt ipsum Lorem est qui cupidatat. Ipsum irure ullamco do cillum duis est in officia et officia elit nisi dolore cillum. Occaecat eu ad pariatur ad Lorem incididunt exercitation nulla. Adipisicing id mollit labore incididunt eiusmod. Esse voluptate ad cupidatat est commodo velit velit laboris ad fugiat mollit.\r\n", |
||||
"registered": "2003-01-12T07:36:55 -01:00", |
||||
"latitude": 49.58874, |
||||
"longitude": 140.795001, |
||||
"tags": [ |
||||
"nulla", |
||||
"officia", |
||||
"amet", |
||||
"irure", |
||||
"voluptate", |
||||
"exercitation", |
||||
"ad" |
||||
], |
||||
"friends": [ |
||||
{ |
||||
"id": 0, |
||||
"name": "Twila Holcomb" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"name": "Katy Cruz" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"name": "Winnie Savage" |
||||
} |
||||
], |
||||
"randomArrayItem": "apple" |
||||
}, |
||||
{ |
||||
"id": 9, |
||||
"guid": "bf01372f-e27c-4f56-9831-9599cc11a73a", |
||||
"isActive": true, |
||||
"balance": "$1,087.00", |
||||
"picture": "http://placehold.it/32x32", |
||||
"age": 37, |
||||
"name": "Boyd Tyson", |
||||
"gender": "male", |
||||
"company": "Skyplex", |
||||
"email": "boydtyson@skyplex.com", |
||||
"phone": "+1 (913) 438-2918", |
||||
"address": "966 Winthrop Street, Weogufka, Massachusetts, 2510", |
||||
"about": "Pariatur occaecat eiusmod incididunt elit irure sit enim proident et aute aliquip quis ea exercitation. Cillum et labore est anim sunt. Sint consectetur ullamco exercitation sit consequat laborum exercitation ea dolor deserunt. Proident fugiat ipsum voluptate commodo mollit pariatur commodo veniam eu ex duis nulla. Ex officia qui occaecat duis consectetur. Ullamco fugiat adipisicing quis Lorem velit laborum officia ex veniam id excepteur in.\r\n", |
||||
"registered": "2000-12-30T18:33:42 -01:00", |
||||
"latitude": 17.222712, |
||||
"longitude": 110.698092, |
||||
"tags": [ |
||||
"esse", |
||||
"consequat", |
||||
"dolor", |
||||
"proident", |
||||
"Lorem", |
||||
"ex", |
||||
"ex" |
||||
], |
||||
"friends": [ |
||||
{ |
||||
"id": 0, |
||||
"name": "Stout Herring" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"name": "Noemi Vincent" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"name": "Johnnie Yates" |
||||
} |
||||
], |
||||
"randomArrayItem": "cherry" |
||||
}, |
||||
{ |
||||
"id": 10, |
||||
"guid": "ff535706-ea8c-44fe-b1aa-ae85c3e88931", |
||||
"isActive": false, |
||||
"balance": "$2,573.00", |
||||
"picture": "http://placehold.it/32x32", |
||||
"age": 34, |
||||
"name": "Lucille Juarez", |
||||
"gender": "female", |
||||
"company": "Essensia", |
||||
"email": "lucillejuarez@essensia.com", |
||||
"phone": "+1 (842) 518-3032", |
||||
"address": "976 Lexington Avenue, Jacumba, Tennessee, 2110", |
||||
"about": "Minim esse in nostrud et dolor ut dolore dolor. Eiusmod sint incididunt id dolore elit eu quis dolor. Reprehenderit occaecat reprehenderit minim aute tempor sit. Voluptate sunt eiusmod sit id cillum magna aliquip aliquip. Magna cillum qui id adipisicing irure quis laboris.\r\n", |
||||
"registered": "2002-08-18T00:54:13 -02:00", |
||||
"latitude": 23.945548, |
||||
"longitude": 34.181728, |
||||
"tags": [ |
||||
"veniam", |
||||
"ex", |
||||
"aliquip", |
||||
"ex", |
||||
"ad", |
||||
"reprehenderit", |
||||
"et" |
||||
], |
||||
"friends": [ |
||||
{ |
||||
"id": 0, |
||||
"name": "Norma Weiss" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"name": "Elnora Odom" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"name": "Jennifer Puckett" |
||||
} |
||||
], |
||||
"randomArrayItem": "lemon" |
||||
}, |
||||
{ |
||||
"id": 11, |
||||
"guid": "30eb71bc-e76e-41eb-90db-b6607b2471b5", |
||||
"isActive": true, |
||||
"balance": "$3,019.00", |
||||
"picture": "http://placehold.it/32x32", |
||||
"age": 27, |
||||
"name": "Antonia Byers", |
||||
"gender": "female", |
||||
"company": "Pearlessa", |
||||
"email": "antoniabyers@pearlessa.com", |
||||
"phone": "+1 (986) 456-2695", |
||||
"address": "112 Montieth Street, Hegins, Arkansas, 614", |
||||
"about": "Consequat id Lorem pariatur sunt ea veniam anim aliquip amet duis. Minim id nulla est est cupidatat laboris commodo. Et eu magna commodo commodo esse ut deserunt cupidatat ea velit. Sint duis voluptate non amet ea laborum. Dolore ullamco ut Lorem aliquip pariatur laboris. Laborum deserunt Lorem sit eu elit duis exercitation proident sit proident.\r\n", |
||||
"registered": "2004-01-17T16:00:50 -01:00", |
||||
"latitude": -69.440764, |
||||
"longitude": -135.627868, |
||||
"tags": [ |
||||
"adipisicing", |
||||
"voluptate", |
||||
"incididunt", |
||||
"laboris", |
||||
"veniam", |
||||
"commodo", |
||||
"velit" |
||||
], |
||||
"friends": [ |
||||
{ |
||||
"id": 0, |
||||
"name": "Melba Fields" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"name": "Miles Holden" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"name": "Lauren Goff" |
||||
} |
||||
], |
||||
"randomArrayItem": "apple" |
||||
}, |
||||
{ |
||||
"id": 12, |
||||
"guid": "5d1f9fd9-b3a6-4320-ba8a-cb9273bca3e9", |
||||
"isActive": false, |
||||
"balance": "$1,723.00", |
||||
"picture": "http://placehold.it/32x32", |
||||
"age": 28, |
||||
"name": "Wyatt Douglas", |
||||
"gender": "male", |
||||
"company": "Paragonia", |
||||
"email": "wyattdouglas@paragonia.com", |
||||
"phone": "+1 (885) 580-2488", |
||||
"address": "674 Gerritsen Avenue, Needmore, Michigan, 608", |
||||
"about": "Enim adipisicing ut proident occaecat cupidatat excepteur sint do id aute ad amet laboris est. Sit culpa consequat nisi sunt exercitation ullamco est commodo exercitation fugiat velit. Laboris et sit deserunt commodo exercitation nisi aliqua non incididunt id id. Tempor exercitation aute Lorem anim ad nulla dolor aliqua do dolore.\r\n", |
||||
"registered": "1991-09-02T02:08:08 -02:00", |
||||
"latitude": 66.105776, |
||||
"longitude": 91.843229, |
||||
"tags": [ |
||||
"consectetur", |
||||
"sunt", |
||||
"ex", |
||||
"enim", |
||||
"labore", |
||||
"esse", |
||||
"ut" |
||||
], |
||||
"friends": [ |
||||
{ |
||||
"id": 0, |
||||
"name": "Mcpherson Velez" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"name": "Cochran Bennett" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"name": "Welch Glover" |
||||
} |
||||
], |
||||
"randomArrayItem": "cherry" |
||||
}, |
||||
{ |
||||
"id": 13, |
||||
"guid": "139ebd33-41d1-4a12-aaf0-e2957587ea9f", |
||||
"isActive": true, |
||||
"balance": "$1,838.00", |
||||
"picture": "http://placehold.it/32x32", |
||||
"age": 26, |
||||
"name": "Leah Downs", |
||||
"gender": "female", |
||||
"company": "Idealis", |
||||
"email": "leahdowns@idealis.com", |
||||
"phone": "+1 (878) 537-3328", |
||||
"address": "174 Herkimer Court, Hamilton, Indiana, 8465", |
||||
"about": "Elit deserunt ipsum exercitation dolor cillum excepteur culpa culpa ut sunt veniam. Elit non ut magna ea nostrud excepteur nisi aliquip pariatur. Veniam culpa officia ea nostrud minim et eiusmod. Sunt aliquip in non pariatur eu dolore aliquip magna. Velit exercitation et nisi deserunt exercitation voluptate Lorem enim elit fugiat adipisicing excepteur aliquip ut. Duis id reprehenderit ullamco commodo nulla nisi officia commodo occaecat sunt tempor veniam. Cillum minim do adipisicing cupidatat anim officia velit elit ea magna nisi do enim reprehenderit.\r\n", |
||||
"registered": "1988-08-25T19:07:19 -02:00", |
||||
"latitude": -73.977289, |
||||
"longitude": -116.135149, |
||||
"tags": [ |
||||
"ex", |
||||
"et", |
||||
"non", |
||||
"commodo", |
||||
"cupidatat", |
||||
"cupidatat", |
||||
"nisi" |
||||
], |
||||
"friends": [ |
||||
{ |
||||
"id": 0, |
||||
"name": "Soto Brown" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"name": "Henry Conrad" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"name": "Marshall Wolf" |
||||
} |
||||
], |
||||
"randomArrayItem": "apple" |
||||
}, |
||||
{ |
||||
"id": 14, |
||||
"guid": "1082a62a-c042-4826-a50a-b5be72a07037", |
||||
"isActive": false, |
||||
"balance": "$3,349.00", |
||||
"picture": "http://placehold.it/32x32", |
||||
"age": 29, |
||||
"name": "Schneider Durham", |
||||
"gender": "male", |
||||
"company": "Memora", |
||||
"email": "schneiderdurham@memora.com", |
||||
"phone": "+1 (882) 421-2055", |
||||
"address": "166 Chauncey Street, Coultervillle, Idaho, 8121", |
||||
"about": "Aute laboris nisi elit do dolor tempor reprehenderit cillum esse non consectetur nostrud voluptate. Tempor mollit do dolor irure non in deserunt ut proident nisi cupidatat. Anim mollit labore quis nulla nostrud reprehenderit sit aliqua exercitation nisi.\r\n", |
||||
"registered": "2003-09-21T21:15:56 -02:00", |
||||
"latitude": -56.159995, |
||||
"longitude": 4.310579, |
||||
"tags": [ |
||||
"adipisicing", |
||||
"ex", |
||||
"exercitation", |
||||
"aliquip", |
||||
"commodo", |
||||
"non", |
||||
"excepteur" |
||||
], |
||||
"friends": [ |
||||
{ |
||||
"id": 0, |
||||
"name": "Jo Lara" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"name": "Merrill Gray" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"name": "Jacobson Burris" |
||||
} |
||||
], |
||||
"randomArrayItem": "lemon" |
||||
}, |
||||
{ |
||||
"id": 15, |
||||
"guid": "b58c304c-cca9-482c-b2b5-810e8fa15ef6", |
||||
"isActive": true, |
||||
"balance": "$2,923.00", |
||||
"picture": "http://placehold.it/32x32", |
||||
"age": 30, |
||||
"name": "Shelton Burnett", |
||||
"gender": "male", |
||||
"company": "Geekola", |
||||
"email": "sheltonburnett@geekola.com", |
||||
"phone": "+1 (821) 501-3726", |
||||
"address": "117 Chase Court, Joes, Alabama, 774", |
||||
"about": "Non sit cupidatat laboris deserunt adipisicing nisi ipsum commodo. Labore duis sint eu dolor esse consectetur esse ullamco Lorem id nisi culpa sint. Cillum commodo incididunt occaecat reprehenderit enim aliquip velit. Sit voluptate dolore deserunt magna voluptate laborum fugiat ad. Voluptate ad aliqua ullamco eiusmod. Labore ut non ut reprehenderit enim non.\r\n", |
||||
"registered": "1992-10-02T12:42:44 -01:00", |
||||
"latitude": -58.765178, |
||||
"longitude": -155.109015, |
||||
"tags": [ |
||||
"culpa", |
||||
"do", |
||||
"do", |
||||
"enim", |
||||
"minim", |
||||
"enim", |
||||
"nulla" |
||||
], |
||||
"friends": [ |
||||
{ |
||||
"id": 0, |
||||
"name": "Lillie Todd" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"name": "Morales Crosby" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"name": "Tracie Hamilton" |
||||
} |
||||
], |
||||
"randomArrayItem": "apple" |
||||
}, |
||||
{ |
||||
"id": 16, |
||||
"guid": "1a33e0ff-909e-4e31-b4f7-a6d0d0d72320", |
||||
"isActive": false, |
||||
"balance": "$1,693.00", |
||||
"picture": "http://placehold.it/32x32", |
||||
"age": 27, |
||||
"name": "Crane Livingston", |
||||
"gender": "male", |
||||
"company": "Enomen", |
||||
"email": "cranelivingston@enomen.com", |
||||
"phone": "+1 (990) 563-2727", |
||||
"address": "248 Elliott Place, Walland, New Jersey, 8005", |
||||
"about": "Culpa anim sunt esse commodo deserunt reprehenderit laboris commodo ullamco Lorem. Tempor veniam officia eiusmod excepteur pariatur quis excepteur qui dolore consequat. Dolore tempor duis occaecat deserunt aute elit ex quis do occaecat et.\r\n", |
||||
"registered": "2007-03-09T12:39:06 -01:00", |
||||
"latitude": 45.176807, |
||||
"longitude": -2.485176, |
||||
"tags": [ |
||||
"ad", |
||||
"ea", |
||||
"in", |
||||
"consequat", |
||||
"aute", |
||||
"laborum", |
||||
"commodo" |
||||
], |
||||
"friends": [ |
||||
{ |
||||
"id": 0, |
||||
"name": "Katrina Hoover" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"name": "Angeline Farmer" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"name": "Perkins Haney" |
||||
} |
||||
], |
||||
"randomArrayItem": "apple" |
||||
}, |
||||
{ |
||||
"id": 17, |
||||
"guid": "e6e7cbf1-e991-4e7b-96b7-bef35f249ffd", |
||||
"isActive": true, |
||||
"balance": "$2,167.00", |
||||
"picture": "http://placehold.it/32x32", |
||||
"age": 21, |
||||
"name": "Bean Moody", |
||||
"gender": "male", |
||||
"company": "Terascape", |
||||
"email": "beanmoody@terascape.com", |
||||
"phone": "+1 (898) 582-2002", |
||||
"address": "773 Brighton Court, Kenwood, Missouri, 6928", |
||||
"about": "Id consequat cupidatat ex cupidatat eiusmod dolore ea incididunt sunt deserunt. Consequat dolor veniam amet incididunt Lorem exercitation dolore do velit tempor id culpa eiusmod. Occaecat esse est laboris nisi consectetur minim. Exercitation ea nostrud laboris aliqua consectetur aliquip nostrud dolor commodo. Elit officia quis in elit enim cupidatat sit sint deserunt dolore duis.\r\n", |
||||
"registered": "1997-10-19T10:30:44 -02:00", |
||||
"latitude": 48.493501, |
||||
"longitude": -53.665346, |
||||
"tags": [ |
||||
"duis", |
||||
"proident", |
||||
"eu", |
||||
"dolor", |
||||
"occaecat", |
||||
"sit", |
||||
"eu" |
||||
], |
||||
"friends": [ |
||||
{ |
||||
"id": 0, |
||||
"name": "Beasley Mcmillan" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"name": "Saundra Morales" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"name": "Morrow Mcclure" |
||||
} |
||||
], |
||||
"randomArrayItem": "lemon" |
||||
}, |
||||
{ |
||||
"id": 18, |
||||
"guid": "53d4712a-aaad-4cf7-8de9-a0e2422a070c", |
||||
"isActive": true, |
||||
"balance": "$1,193.00", |
||||
"picture": "http://placehold.it/32x32", |
||||
"age": 20, |
||||
"name": "Hodges Lawson", |
||||
"gender": "male", |
||||
"company": "Medesign", |
||||
"email": "hodgeslawson@medesign.com", |
||||
"phone": "+1 (843) 453-3635", |
||||
"address": "216 Fairview Place, Balm, Louisiana, 5648", |
||||
"about": "Proident aliqua exercitation minim est. Esse veniam sunt dolore laborum dolor minim. Dolor id eiusmod fugiat qui anim incididunt occaecat pariatur non. Ea exercitation incididunt elit non eu.\r\n", |
||||
"registered": "2008-01-14T06:12:50 -01:00", |
||||
"latitude": 77.014486, |
||||
"longitude": 171.670465, |
||||
"tags": [ |
||||
"dolor", |
||||
"ullamco", |
||||
"minim", |
||||
"sunt", |
||||
"laborum", |
||||
"dolor", |
||||
"ex" |
||||
], |
||||
"friends": [ |
||||
{ |
||||
"id": 0, |
||||
"name": "Mcconnell Hahn" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"name": "King Baker" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"name": "Virgie Holt" |
||||
} |
||||
], |
||||
"randomArrayItem": "cherry" |
||||
}, |
||||
{ |
||||
"id": 19, |
||||
"guid": "99db9ee5-bd4c-41f6-9f73-92f5644b7b97", |
||||
"isActive": true, |
||||
"balance": "$3,744.00", |
||||
"picture": "http://placehold.it/32x32", |
||||
"age": 40, |
||||
"name": "Lancaster Walton", |
||||
"gender": "male", |
||||
"company": "Codax", |
||||
"email": "lancasterwalton@codax.com", |
||||
"phone": "+1 (965) 409-3192", |
||||
"address": "646 Harwood Place, Bodega, New Hampshire, 606", |
||||
"about": "Proident nostrud enim consequat irure. Duis amet incididunt ex do quis consectetur nisi laboris est voluptate esse. Laboris consectetur esse ullamco nostrud aute fugiat ad dolore eiusmod. Sit commodo elit aute Lorem ad deserunt sit est Lorem reprehenderit dolor minim ex.\r\n", |
||||
"registered": "2011-03-05T16:51:53 -01:00", |
||||
"latitude": -67.193873, |
||||
"longitude": 45.464244, |
||||
"tags": [ |
||||
"qui", |
||||
"ea", |
||||
"laborum", |
||||
"voluptate", |
||||
"occaecat", |
||||
"nisi", |
||||
"in" |
||||
], |
||||
"friends": [ |
||||
{ |
||||
"id": 0, |
||||
"name": "Zamora Trujillo" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"name": "Latisha Fernandez" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"name": "Mayo Harris" |
||||
} |
||||
], |
||||
"randomArrayItem": "apple" |
||||
}, |
||||
{ |
||||
"id": 20, |
||||
"guid": "d54147b7-09fe-4dfb-8828-a73a5d2654b9", |
||||
"isActive": true, |
||||
"balance": "$1,534.00", |
||||
"picture": "http://placehold.it/32x32", |
||||
"age": 36, |
||||
"name": "Lindsay Hawkins", |
||||
"gender": "male", |
||||
"company": "Isosphere", |
||||
"email": "lindsayhawkins@isosphere.com", |
||||
"phone": "+1 (957) 416-3291", |
||||
"address": "461 Conover Street, Epworth, Wyoming, 2303", |
||||
"about": "Enim amet sint minim pariatur aliquip proident fugiat consequat deserunt proident. Do excepteur Lorem velit sint proident ea aute eiusmod ipsum. Laborum id eiusmod do nostrud proident consequat eu fugiat elit nostrud nisi. Id aliqua est aliqua dolore ad id commodo est cillum in ipsum nostrud. Enim anim velit id adipisicing non exercitation dolore proident. Dolor sit amet commodo duis.\r\n", |
||||
"registered": "1991-07-10T19:55:25 -02:00", |
||||
"latitude": -71.393384, |
||||
"longitude": 129.718457, |
||||
"tags": [ |
||||
"nostrud", |
||||
"reprehenderit", |
||||
"ea", |
||||
"elit", |
||||
"ipsum", |
||||
"cillum", |
||||
"nulla" |
||||
], |
||||
"friends": [ |
||||
{ |
||||
"id": 0, |
||||
"name": "Bartlett Shaw" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"name": "Black Barron" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"name": "Nona Mcgee" |
||||
} |
||||
], |
||||
"randomArrayItem": "apple" |
||||
}, |
||||
{ |
||||
"id": 21, |
||||
"guid": "d648f647-adaa-4bbe-9285-344a916f2241", |
||||
"isActive": true, |
||||
"balance": "$1,387.00", |
||||
"picture": "http://placehold.it/32x32", |
||||
"age": 22, |
||||
"name": "Jewel Mccoy", |
||||
"gender": "female", |
||||
"company": "Tetak", |
||||
"email": "jewelmccoy@tetak.com", |
||||
"phone": "+1 (931) 568-2889", |
||||
"address": "691 Putnam Avenue, Nettie, Arizona, 7175", |
||||
"about": "Enim proident occaecat culpa cupidatat exercitation aute ullamco aliquip id in et ea sit enim. Id Lorem veniam minim laborum labore minim fugiat eu anim. Eiusmod esse magna sunt ea. Proident deserunt dolore consequat mollit culpa laboris. Tempor veniam fugiat eu aliqua. Sint velit consequat in tempor velit non ullamco consequat duis dolore mollit cupidatat in consectetur. Culpa consequat et non nostrud amet fugiat ea do sit est reprehenderit incididunt aute.\r\n", |
||||
"registered": "2013-09-21T12:52:13 -02:00", |
||||
"latitude": 12.704629, |
||||
"longitude": 96.065025, |
||||
"tags": [ |
||||
"irure", |
||||
"aliqua", |
||||
"excepteur", |
||||
"elit", |
||||
"occaecat", |
||||
"nostrud", |
||||
"dolore" |
||||
], |
||||
"friends": [ |
||||
{ |
||||
"id": 0, |
||||
"name": "Ida Harvey" |
||||
}, |
||||
{ |
||||
"id": 1, |
||||
"name": "Ora Santana" |
||||
}, |
||||
{ |
||||
"id": 2, |
||||
"name": "Lynnette Bullock" |
||||
} |
||||
], |
||||
"randomArrayItem": "lemon" |
||||
} |
||||
] |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1 @@
|
||||
{
"store": {
"book": [
{
"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"display-price": 8.95
},
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"display-price": 12.99
},
{
"category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"display-price": 8.99
},
{
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"display-price": 22.99
}
],
"bicycle": {
"foo": "baz",
"color": "red",
"display-price": 19.95,
"foo:bar": "fooBar",
"dot.notation": "new",
"dash-notation": "dashes"
}
},
"foo": "bar",
"@id": "ID"
} |
@ -0,0 +1,298 @@
|
||||
{"completed_in": 0.072, "max_id": 336448826225328128, "max_id_str": "336448826225328128", "next_page": "?page=2&max_id=336448826225328128&q=gatling", "page": 1, "query": "gatling", "refresh_url": "?since_id=336448826225328128&q=gatling", "results": [ |
||||
{ |
||||
"created_at": "Mon, 20 May 2013 11:50:25 +0000", |
||||
"from_user": "anna_gatling", |
||||
"from_user_id": 1237619628, |
||||
"from_user_id_str": "1237619628", |
||||
"from_user_name": "Anna Gatling", |
||||
"geo": null, |
||||
"id": 336448826225328128, |
||||
"id_str": "336448826225328128", |
||||
"iso_language_code": "en", |
||||
"metadata": { |
||||
"result_type": "recent" |
||||
}, |
||||
"profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/3402698946\/bebb3fcb7a7a3977a69c30797cfce5db_normal.jpeg", |
||||
"profile_image_url_https": "https:\/\/si0.twimg.com\/profile_images\/3402698946\/bebb3fcb7a7a3977a69c30797cfce5db_normal.jpeg", |
||||
"source": "<a href="http:\/\/twitter.com\/download\/iphone">Twitter for iPhone<\/a>", |
||||
"text": "Last Monday of 6th grade yayyyyy" |
||||
}, |
||||
{ |
||||
"created_at": "Mon, 20 May 2013 11:49:40 +0000", |
||||
"from_user": "ddnn_", |
||||
"from_user_id": 482530993, |
||||
"from_user_id_str": "482530993", |
||||
"from_user_name": "D'\u306e\u7d14\u60c5", |
||||
"geo": null, |
||||
"id": 336448635355144192, |
||||
"id_str": "336448635355144192", |
||||
"iso_language_code": "tl", |
||||
"metadata": { |
||||
"result_type": "recent" |
||||
}, |
||||
"profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/3676715268\/74fa7666ffce02296cc7dfa25f6a8f9c_normal.png", |
||||
"profile_image_url_https": "https:\/\/si0.twimg.com\/profile_images\/3676715268\/74fa7666ffce02296cc7dfa25f6a8f9c_normal.png", |
||||
"source": "<a href="http:\/\/m.ubersocial.com">UberSocial Mobile<\/a>", |
||||
"text": "@Arc46_ gatling gun... as in minigun or what?", |
||||
"to_user": "Arc46_", |
||||
"to_user_id": 336528266, |
||||
"to_user_id_str": "336528266", |
||||
"to_user_name": "SamuelJunioPradipta", |
||||
"in_reply_to_status_id": 336448069233172480, |
||||
"in_reply_to_status_id_str": "336448069233172480" |
||||
}, |
||||
{ |
||||
"created_at": "Mon, 20 May 2013 11:48:56 +0000", |
||||
"from_user": "FonshudelSur", |
||||
"from_user_id": 300933633, |
||||
"from_user_id_str": "300933633", |
||||
"from_user_name": "Fonshu", |
||||
"geo": null, |
||||
"id": 336448452076634113, |
||||
"id_str": "336448452076634113", |
||||
"iso_language_code": "es", |
||||
"metadata": { |
||||
"result_type": "recent" |
||||
}, |
||||
"profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/3654066775\/8da1e0e3f2d9ef89f34ac66993bb8836_normal.jpeg", |
||||
"profile_image_url_https": "https:\/\/si0.twimg.com\/profile_images\/3654066775\/8da1e0e3f2d9ef89f34ac66993bb8836_normal.jpeg", |
||||
"source": "<a href="http:\/\/www.tweetdeck.com">TweetDeck<\/a>", |
||||
"text": "@IsaraMiau Y tengo que currarme un cosplay para ir del patriota mecanizado con la gatling steampunk, que es DE LO MEJOR <3", |
||||
"to_user": "FonshudelSur", |
||||
"to_user_id": 300933633, |
||||
"to_user_id_str": "300933633", |
||||
"to_user_name": "Fonshu", |
||||
"in_reply_to_status_id": 336448304835608576, |
||||
"in_reply_to_status_id_str": "336448304835608576" |
||||
}, |
||||
{ |
||||
"created_at": "Mon, 20 May 2013 11:47:25 +0000", |
||||
"from_user": "Arc46_", |
||||
"from_user_id": 336528266, |
||||
"from_user_id_str": "336528266", |
||||
"from_user_name": "SamuelJunioPradipta", |
||||
"geo": null, |
||||
"id": 336448069233172480, |
||||
"id_str": "336448069233172480", |
||||
"iso_language_code": "en", |
||||
"metadata": { |
||||
"result_type": "recent" |
||||
}, |
||||
"profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/3684084503\/2e880c5f9b9d7d6030d6d972feb9f5c4_normal.jpeg", |
||||
"profile_image_url_https": "https:\/\/si0.twimg.com\/profile_images\/3684084503\/2e880c5f9b9d7d6030d6d972feb9f5c4_normal.jpeg", |
||||
"source": "<a href="http:\/\/www.tweetdeck.com">TweetDeck<\/a>", |
||||
"text": "Something like Gatling gun, Bazooka RT @ddnn_: what are you exactly trying to make?" |
||||
}, |
||||
{ |
||||
"created_at": "Mon, 20 May 2013 11:37:49 +0000", |
||||
"from_user": "anna_gatling", |
||||
"from_user_id": 1237619628, |
||||
"from_user_id_str": "1237619628", |
||||
"from_user_name": "Anna Gatling", |
||||
"geo": null, |
||||
"id": 336445653662191616, |
||||
"id_str": "336445653662191616", |
||||
"iso_language_code": "en", |
||||
"metadata": { |
||||
"result_type": "recent" |
||||
}, |
||||
"profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/3402698946\/bebb3fcb7a7a3977a69c30797cfce5db_normal.jpeg", |
||||
"profile_image_url_https": "https:\/\/si0.twimg.com\/profile_images\/3402698946\/bebb3fcb7a7a3977a69c30797cfce5db_normal.jpeg", |
||||
"source": "<a href="http:\/\/twitter.com\/download\/iphone">Twitter for iPhone<\/a>", |
||||
"text": "Mondays<" |
||||
}, |
||||
{ |
||||
"created_at": "Mon, 20 May 2013 11:29:57 +0000", |
||||
"from_user": "GATLING_FIGHTER", |
||||
"from_user_id": 1184500015, |
||||
"from_user_id_str": "1184500015", |
||||
"from_user_name": "M134 \u307f\u306b-\u304c\u3093", |
||||
"geo": null, |
||||
"id": 336443676270157826, |
||||
"id_str": "336443676270157826", |
||||
"iso_language_code": "ja", |
||||
"metadata": { |
||||
"result_type": "recent" |
||||
}, |
||||
"profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/3607829622\/68059740f01d6532bc876e2e7ffb84d0_normal.jpeg", |
||||
"profile_image_url_https": "https:\/\/si0.twimg.com\/profile_images\/3607829622\/68059740f01d6532bc876e2e7ffb84d0_normal.jpeg", |
||||
"source": "<a href="http:\/\/theworld09.com\/">TheWorld\u2800<\/a>", |
||||
"text": "@Orz619 \u4f55\u3067\u3059\u3063\u3066\uff01\uff1f", |
||||
"to_user": "Orz619", |
||||
"to_user_id": 1393895851, |
||||
"to_user_id_str": "1393895851", |
||||
"to_user_name": "\u3050\u3063\u3055\u3093@\u9ed2\u732b\u306f\u53f7\u54ed\u3057\u3066\u3044\u307e\u3059\u3088\u3002", |
||||
"in_reply_to_status_id": 336443439870779394, |
||||
"in_reply_to_status_id_str": "336443439870779394" |
||||
}, |
||||
{ |
||||
"created_at": "Mon, 20 May 2013 11:29:48 +0000", |
||||
"from_user": "GATLING_FIGHTER", |
||||
"from_user_id": 1184500015, |
||||
"from_user_id_str": "1184500015", |
||||
"from_user_name": "M134 \u307f\u306b-\u304c\u3093", |
||||
"geo": null, |
||||
"id": 336443634872360960, |
||||
"id_str": "336443634872360960", |
||||
"iso_language_code": "ja", |
||||
"metadata": { |
||||
"result_type": "recent" |
||||
}, |
||||
"profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/3607829622\/68059740f01d6532bc876e2e7ffb84d0_normal.jpeg", |
||||
"profile_image_url_https": "https:\/\/si0.twimg.com\/profile_images\/3607829622\/68059740f01d6532bc876e2e7ffb84d0_normal.jpeg", |
||||
"source": "<a href="http:\/\/theworld09.com\/">TheWorld\u2800<\/a>", |
||||
"text": "RT @Orz619: \u307b\u3093\u304d\u3067\u30b9\u30d1\u30d6\u30ed\u3057\u3088\u3046\u304b\u3068\u691c\u8a0e\u4e2dw" |
||||
}, |
||||
{ |
||||
"created_at": "Mon, 20 May 2013 10:46:41 +0000", |
||||
"from_user": "Gatling_gun_k", |
||||
"from_user_id": 1126180920, |
||||
"from_user_id_str": "1126180920", |
||||
"from_user_name": "\uac1c\ud2c0\ub9c1 \uae30\uad00\ucd1d", |
||||
"geo": null, |
||||
"id": 336432787248787456, |
||||
"id_str": "336432787248787456", |
||||
"iso_language_code": "ko", |
||||
"metadata": { |
||||
"result_type": "recent" |
||||
}, |
||||
"profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/3171674022\/d318e014542845396c11662986b4285d_normal.png", |
||||
"profile_image_url_https": "https:\/\/si0.twimg.com\/profile_images\/3171674022\/d318e014542845396c11662986b4285d_normal.png", |
||||
"source": "<a href="http:\/\/twittbot.net\/">twittbot.net<\/a>", |
||||
"text": "\ud0c0\ud0c0\ud0c0\ud0c0\ud0c0\ud0c0\ud0c0\ud0c0\ud0c0\ud0c0\ud0c0\ud0c0\ud0c0\ud0c0!!" |
||||
}, |
||||
{ |
||||
"created_at": "Mon, 20 May 2013 10:22:40 +0000", |
||||
"from_user": "slam_gatling", |
||||
"from_user_id": 1316243516, |
||||
"from_user_id_str": "1316243516", |
||||
"from_user_name": "SxOxE", |
||||
"geo": null, |
||||
"id": 336426741893578754, |
||||
"id_str": "336426741893578754", |
||||
"iso_language_code": "ja", |
||||
"metadata": { |
||||
"result_type": "recent" |
||||
}, |
||||
"profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/3606289395\/106dee12ccd2fb0bca99ba4b85304d54_normal.jpeg", |
||||
"profile_image_url_https": "https:\/\/si0.twimg.com\/profile_images\/3606289395\/106dee12ccd2fb0bca99ba4b85304d54_normal.jpeg", |
||||
"source": "<a href="http:\/\/twitter.com\/">web<\/a>", |
||||
"text": "\u59b9\u304cHUNTER\u00d7HUNTER\u3092\u8aad\u3093\u3067\u308b\u305b\u3044\u304b\u3001\u4ffa\u3082\u4e2d\u5b66\u4ee5\u6765\u4e45\u3005\u306b\u30cf\u30de\u3063\u305f\u3002\u6697\u9ed2\u5927\u9678\u7de8\u304c\u3069\u3046\u306a\u308b\u304b\u6c17\u306b\u306a\u308b\u3051\u3069\u3001\u5927\u66ae\u7dad\u4eba\u307f\u305f\u3044\u306b\u8a71\u3092\u30c7\u30ab\u304f\u3057\u3059\u304e\u3066\u53ce\u96c6\u3064\u304b\u306a\u304f\u306a\u308b\u53ef\u80fd\u6027\u3082\u5fae\u30ec\u5b58\u3002\u3042\u3068\u30af\u30e9\u30d4\u30ab\u304c\u4eca\u5f8c\u3069\u3046\u95a2\u308f\u3063\u3066\u304f\u308b\u306e\u304b\u3082\u6c17\u306b\u306a\u308b\u3068\u3053\u308d\u3002" |
||||
}, |
||||
{ |
||||
"created_at": "Mon, 20 May 2013 09:46:45 +0000", |
||||
"from_user": "Gatling_gun_k", |
||||
"from_user_id": 1126180920, |
||||
"from_user_id_str": "1126180920", |
||||
"from_user_name": "\uac1c\ud2c0\ub9c1 \uae30\uad00\ucd1d", |
||||
"geo": null, |
||||
"id": 336417701356507136, |
||||
"id_str": "336417701356507136", |
||||
"iso_language_code": "ko", |
||||
"metadata": { |
||||
"result_type": "recent" |
||||
}, |
||||
"profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/3171674022\/d318e014542845396c11662986b4285d_normal.png", |
||||
"profile_image_url_https": "https:\/\/si0.twimg.com\/profile_images\/3171674022\/d318e014542845396c11662986b4285d_normal.png", |
||||
"source": "<a href="http:\/\/twittbot.net\/">twittbot.net<\/a>", |
||||
"text": "\ud22c\ud22c\ud22c\ud22c\ud22c\ud22c\ud22c\ud22c\ud22c\ud22c\ud22c\ud22c\ud22c\ud22c\ud22c!!" |
||||
}, |
||||
{ |
||||
"created_at": "Mon, 20 May 2013 09:17:47 +0000", |
||||
"from_user": "Gatling_gun_k", |
||||
"from_user_id": 1126180920, |
||||
"from_user_id_str": "1126180920", |
||||
"from_user_name": "\uac1c\ud2c0\ub9c1 \uae30\uad00\ucd1d", |
||||
"geo": null, |
||||
"id": 336410413707169792, |
||||
"id_str": "336410413707169792", |
||||
"iso_language_code": "ko", |
||||
"metadata": { |
||||
"result_type": "recent" |
||||
}, |
||||
"profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/3171674022\/d318e014542845396c11662986b4285d_normal.png", |
||||
"profile_image_url_https": "https:\/\/si0.twimg.com\/profile_images\/3171674022\/d318e014542845396c11662986b4285d_normal.png", |
||||
"source": "<a href="http:\/\/twittbot.net\/">twittbot.net<\/a>", |
||||
"text": "\ub69c\ub450\ub450\ub450\ub457\ub450\ub450\ub450\ub450\ub450\ub450\ub450\ub450\ub450\ub450!!" |
||||
}, |
||||
{ |
||||
"created_at": "Mon, 20 May 2013 09:03:00 +0000", |
||||
"from_user": "Rebelzonderrede", |
||||
"from_user_id": 983435376, |
||||
"from_user_id_str": "983435376", |
||||
"from_user_name": "Rebel South", |
||||
"geo": null, |
||||
"id": 336406693384708096, |
||||
"id_str": "336406693384708096", |
||||
"iso_language_code": "nl", |
||||
"metadata": { |
||||
"result_type": "recent" |
||||
}, |
||||
"profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/2920503711\/6db32e22de8ee8efa92ba1e16ef341b6_normal.jpeg", |
||||
"profile_image_url_https": "https:\/\/si0.twimg.com\/profile_images\/2920503711\/6db32e22de8ee8efa92ba1e16ef341b6_normal.jpeg", |
||||
"source": "<a href="http:\/\/www.tweetdeck.com">TweetDeck<\/a>", |
||||
"text": "20-5-1874: Sterfdag Alexander B. Dyer, Gen vd Unie. 1e Generaal die aanschaf Gatling gun nastreefde #amerikaanseburgeroorlog" |
||||
}, |
||||
{ |
||||
"created_at": "Mon, 20 May 2013 08:45:00 +0000", |
||||
"from_user": "MahdayMayday", |
||||
"from_user_id": 896598301, |
||||
"from_user_id_str": "896598301", |
||||
"from_user_name": "C H E ' N G A P !", |
||||
"geo": null, |
||||
"id": 336402162995310592, |
||||
"id_str": "336402162995310592", |
||||
"iso_language_code": "in", |
||||
"metadata": { |
||||
"result_type": "recent" |
||||
}, |
||||
"profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/3089084467\/b5406e2a17e115108d798220ef872e59_normal.jpeg", |
||||
"profile_image_url_https": "https:\/\/si0.twimg.com\/profile_images\/3089084467\/b5406e2a17e115108d798220ef872e59_normal.jpeg", |
||||
"source": "<a href="http:\/\/twitter.com\/">web<\/a>", |
||||
"text": "RT @mnhfz92: @MahdayMayday ah ye..bg Gomu Gomu no Elephant Gatling kang", |
||||
"in_reply_to_status_id": 336401088506900480, |
||||
"in_reply_to_status_id_str": "336401088506900480" |
||||
}, |
||||
{ |
||||
"created_at": "Mon, 20 May 2013 08:43:55 +0000", |
||||
"from_user": "mnhfz92", |
||||
"from_user_id": 282901250, |
||||
"from_user_id_str": "282901250", |
||||
"from_user_name": "Nor Hafiz", |
||||
"geo": null, |
||||
"id": 336401891959402496, |
||||
"id_str": "336401891959402496", |
||||
"iso_language_code": "in", |
||||
"metadata": { |
||||
"result_type": "recent" |
||||
}, |
||||
"profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/2661900827\/f41fc3f51c58daac67f1aa481de43819_normal.jpeg", |
||||
"profile_image_url_https": "https:\/\/si0.twimg.com\/profile_images\/2661900827\/f41fc3f51c58daac67f1aa481de43819_normal.jpeg", |
||||
"source": "<a href="http:\/\/twitter.com\/">web<\/a>", |
||||
"text": "@MahdayMayday ah ye..bg Gomu Gomu no Elephant Gatling kang", |
||||
"to_user": "MahdayMayday", |
||||
"to_user_id": 896598301, |
||||
"to_user_id_str": "896598301", |
||||
"to_user_name": "C H E ' N G A P !", |
||||
"in_reply_to_status_id": 336401088506900480, |
||||
"in_reply_to_status_id_str": "336401088506900480" |
||||
}, |
||||
{ |
||||
"created_at": "Mon, 20 May 2013 08:00:00 +0000", |
||||
"from_user": "origichara_bot", |
||||
"from_user_id": 410573195, |
||||
"from_user_id_str": "410573195", |
||||
"from_user_name": "\u307f\u3093\u306a\u306e\u30aa\u30ea\u30ad\u30e3\u30e9\u7d39\u4ecbbot", |
||||
"geo": null, |
||||
"id": 336390837846020096, |
||||
"id_str": "336390837846020096", |
||||
"iso_language_code": "ja", |
||||
"metadata": { |
||||
"result_type": "recent" |
||||
}, |
||||
"profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/1634960810\/_____bot_normal.jpg", |
||||
"profile_image_url_https": "https:\/\/si0.twimg.com\/profile_images\/1634960810\/_____bot_normal.jpg", |
||||
"source": "<a href="http:\/\/twittbot.net\/">twittbot.net<\/a>", |
||||
"text": "\u300c\u6551\u4e16\u4e3b(\u30e1\u30b7\u30a2)\u3068\u3088\u3079\u3001\u3044\u3044\u306a\uff1f\u300d\u300c\u9ed9\u308c\u5909\u614b\u5909\u4eba\u30b1\u30df\u30ab\u30eb\u30d6\u30ec\u30a4\u30f3\u5973\u304c\u3002\u79c1\u3060\u3063\u3066\u5143\u306f\u4eba\u9593\u3060\u300d\uff0f\u516b\u30f6\u5cf0\u81e8\u592a\u90ce\uff08\u7537\uff6515\u6b73\u30fb\u81ea\u79f0\u6551\u4e16\u4e3b\u306e\u4e2d\u4e8c\u75c5\u30cf\u30a4\u30ab\u30e9\u5c11\u5e74\uff09http:\/\/t.co\/FS7av2d077" |
||||
} |
||||
], "results_per_page": 15, "since_id": 0, "since_id_str": "0"} |
@ -0,0 +1,100 @@
|
||||
{"web-app": { |
||||
"servlet": [ |
||||
{ |
||||
"servlet-name": "cofaxCDS", |
||||
"servlet-class": "org.cofax.cds.CDSServlet", |
||||
"init-param": { |
||||
"configGlossary:installationAt": "Philadelphia, PA", |
||||
"configGlossary:adminEmail": "ksm@pobox.com", |
||||
"configGlossary:poweredBy": "Cofax", |
||||
"configGlossary:poweredByIcon": "/images/cofax.gif", |
||||
"configGlossary:staticPath": "/content/static", |
||||
"templateProcessorClass": "org.cofax.WysiwygTemplate", |
||||
"templateLoaderClass": "org.cofax.FilesTemplateLoader", |
||||
"templatePath": "templates", |
||||
"templateOverridePath": "", |
||||
"defaultListTemplate": "listTemplate.htm", |
||||
"defaultFileTemplate": "articleTemplate.htm", |
||||
"useJSP": false, |
||||
"jspListTemplate": "listTemplate.jsp", |
||||
"jspFileTemplate": "articleTemplate.jsp", |
||||
"cachePackageTagsTrack": 200, |
||||
"cachePackageTagsStore": 200, |
||||
"cachePackageTagsRefresh": 60, |
||||
"cacheTemplatesTrack": 100, |
||||
"cacheTemplatesStore": 50, |
||||
"cacheTemplatesRefresh": 15, |
||||
"cachePagesTrack": 200, |
||||
"cachePagesStore": 100, |
||||
"cachePagesRefresh": 10, |
||||
"cachePagesDirtyRead": 10, |
||||
"searchEngineListTemplate": "forSearchEnginesList.htm", |
||||
"searchEngineFileTemplate": "forSearchEngines.htm", |
||||
"searchEngineRobotsDb": "WEB-INF/robots.db", |
||||
"useDataStore": true, |
||||
"dataStoreClass": "org.cofax.SqlDataStore", |
||||
"redirectionClass": "org.cofax.SqlRedirection", |
||||
"dataStoreName": "cofax", |
||||
"dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", |
||||
"dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", |
||||
"dataStoreUser": "sa", |
||||
"dataStorePassword": "dataStoreTestQuery", |
||||
"dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", |
||||
"dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", |
||||
"dataStoreInitConns": 10, |
||||
"dataStoreMaxConns": 100, |
||||
"dataStoreConnUsageLimit": 100, |
||||
"dataStoreLogLevel": "debug", |
||||
"maxUrlLength": 500 |
||||
} |
||||
}, |
||||
{ |
||||
"servlet-name": "cofaxEmail", |
||||
"servlet-class": "org.cofax.cds.EmailServlet", |
||||
"init-param": { |
||||
"mailHost": "mail1", |
||||
"mailHostOverride": "mail2" |
||||
} |
||||
}, |
||||
{ |
||||
"servlet-name": "cofaxAdmin", |
||||
"servlet-class": "org.cofax.cds.AdminServlet" |
||||
}, |
||||
|
||||
{ |
||||
"servlet-name": "fileServlet", |
||||
"servlet-class": "org.cofax.cds.FileServlet" |
||||
}, |
||||
{ |
||||
"servlet-name": "cofaxTools", |
||||
"servlet-class": "org.cofax.cms.CofaxToolsServlet", |
||||
"init-param": { |
||||
"templatePath": "toolstemplates/", |
||||
"log": 1, |
||||
"logLocation": "/usr/local/tomcat/logs/CofaxTools.log", |
||||
"logMaxSize": "", |
||||
"dataLog": 1, |
||||
"dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", |
||||
"dataLogMaxSize": "", |
||||
"removePageCache": "/content/admin/remove?cache=pages&id=", |
||||
"removeTemplateCache": "/content/admin/remove?cache=templates&id=", |
||||
"fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", |
||||
"lookInContext": 1, |
||||
"adminGroupID": 4, |
||||
"betaServer": true |
||||
} |
||||
} |
||||
], |
||||
"servlet-mapping": { |
||||
"cofaxCDS": "/", |
||||
"cofaxEmail": "/cofaxutil/aemail/*", |
||||
"cofaxAdmin": "/admin/*", |
||||
"fileServlet": "/static/*", |
||||
"cofaxTools": "/tools/*" |
||||
}, |
||||
|
||||
"taglib": { |
||||
"taglib-uri": "cofax.tld", |
||||
"taglib-location": "/WEB-INF/tlds/cofax.tld" |
||||
} |
||||
}} |
@ -0,0 +1,2 @@
|
||||
org.slf4j.simpleLogger.defaultLogLevel=warn |
||||
org.slf4j.simpleLogger.log.com.jayway=debug |
@ -0,0 +1,118 @@
|
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
||||
<title>JsonPath</title> |
||||
<link rel="stylesheet" href="/static/css/bootstrap.css"> |
||||
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css"> |
||||
|
||||
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> |
||||
<script type="text/javascript" src="/static/js/jsonpath-0.8.0.js"></script> |
||||
<script type="text/javascript" src="/static/js/bootstrap.js"></script> |
||||
<script src="//google-code-prettify.googlecode.com/svn/loader/run_prettify.js"></script> |
||||
</head> |
||||
<body role="document"> |
||||
<div class="container"> |
||||
<div class="jumbotron"> |
||||
<h3>JSONPath evaluator</h3> |
||||
</div> |
||||
<div class="row"> |
||||
<div class="col-md-6"> |
||||
<form method="post"> |
||||
<div class="form-group"> |
||||
<select id="templates" name="template" class="form-control" onChange='window.location="?template=" + this.value;'> |
||||
{{#templates}} |
||||
<option value="{{value}}" {{selected}}>{{name}}</option> |
||||
{{/templates}} |
||||
</select> |
||||
</div> |
||||
|
||||
<div class="form-group"> |
||||
<textarea id="json" name="json" rows="20" class="form-control">{{json}}</textarea> |
||||
</div> |
||||
|
||||
<div class="radio"> |
||||
<label> |
||||
<input type="radio" name="type" id="optTypeValue" value="value" {{value-checked}} /> |
||||
Matching values |
||||
</label> |
||||
</div> |
||||
<div class="radio"> |
||||
<label> |
||||
<input type="radio" name="type" id="optTypePath" value="path" {{path-checked}} /> |
||||
Normalized path expressions |
||||
</label> |
||||
</div> |
||||
|
||||
<div class="input-group"> |
||||
<input id="path" name="path" value="{{path}}" placeholder="Enter path" class="form-control"/> |
||||
<span class="input-group-btn"> |
||||
<button class="btn btn-default" type="submit">Go!</button> |
||||
</span> |
||||
</div> |
||||
</form> |
||||
</div> |
||||
<div class="col-md-6"> |
||||
<ul id="tabs" class="nav nav-tabs" data-tabs="tabs"> |
||||
<li class="active"><a href="#jayway" data-toggle="tab">Jayway</a></li> |
||||
<li><a href="#boon" data-toggle="tab">Boon</a></li> |
||||
<li><a href="#nebhale" data-toggle="tab">Nebhale</a></li> |
||||
<li><a href="#goessner" data-toggle="tab">Gossner</a></li> |
||||
</ul> |
||||
<div id="my-tab-content" class="tab-content"> |
||||
{{#results}} |
||||
<div class="tab-pane {{active}}" id="{{provider}}"> |
||||
<br/> |
||||
<span id="{{provider}}-time">{{time}}</span> millis |
||||
<hr/> |
||||
{{^error}} |
||||
<div class="row"> |
||||
<div class="col-md-12"> |
||||
<pre class="prettyprint" style="background-color: transparent; border: none;">{{result}}</pre> |
||||
</div> |
||||
</div> |
||||
{{/error}} |
||||
{{#error}} |
||||
<p class="bg-danger">{{error}}</p> |
||||
{{/error}} |
||||
</div> |
||||
{{/results}} |
||||
<div class="tab-pane" id="goessner"> |
||||
<br/> |
||||
<span id="goessner-time"></span> millis |
||||
<hr/> |
||||
<div class="row"> |
||||
<div class="col-md-12"> |
||||
<pre id="goessner-res" class="prettyprint" style="background-color: transparent; border: none;"></pre> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</body> |
||||
<script> |
||||
$( document ).ready(function() { |
||||
if( $('#jayway').length ) |
||||
{ |
||||
//var value = $("#optTypeValue").prop("checked", true); |
||||
var value = $("#optTypeValue").is(':checked'); |
||||
var start = new Date().getTime(); |
||||
var json = JSON.parse($('#json').val()); |
||||
var path = $('#path').val(); |
||||
var res = ''; |
||||
if(value){ |
||||
res = jsonPath(json, path, {resultType:"VALUE"}); |
||||
} else { |
||||
res = jsonPath(json, path, {resultType:"PATH"}); |
||||
} |
||||
var elapsed = new Date().getTime() - start; |
||||
|
||||
$("#goessner-time").html(elapsed); |
||||
$("#goessner-res").text(JSON.stringify(res, null, ' ')); |
||||
} |
||||
}); |
||||
</script> |
||||
</html> |
@ -0,0 +1,618 @@
|
||||
package com.jayway.jsonpath; |
||||
|
||||
import com.jayway.jsonpath.internal.spi.compiler.PathCompiler; |
||||
import com.jayway.jsonpath.spi.compiler.EvaluationContext; |
||||
import com.jayway.jsonpath.spi.compiler.Path; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
import java.math.BigDecimal; |
||||
import java.util.Arrays; |
||||
import java.util.Collection; |
||||
import java.util.LinkedList; |
||||
import java.util.List; |
||||
import java.util.regex.Pattern; |
||||
|
||||
import static com.jayway.jsonpath.internal.Utils.join; |
||||
import static com.jayway.jsonpath.internal.Utils.notNull; |
||||
|
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
public class Criteria2 implements Predicate { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(Criteria2.class); |
||||
|
||||
private final Path path; |
||||
private CriteriaType criteriaType; |
||||
private Object expected; |
||||
|
||||
private final List<Criteria2> criteriaChain; |
||||
|
||||
private enum CriteriaType { |
||||
EQ { |
||||
@Override |
||||
boolean eval(Object expected, Object actual, Configuration configuration) { |
||||
boolean res = (0 == safeCompare(expected, actual, configuration)); |
||||
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res); |
||||
return res; |
||||
} |
||||
}, |
||||
NE { |
||||
@Override |
||||
boolean eval(Object expected, Object actual, Configuration configuration) { |
||||
boolean res = (0 != safeCompare(expected, actual, configuration)); |
||||
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res); |
||||
return res; |
||||
} |
||||
}, |
||||
GT { |
||||
@Override |
||||
boolean eval(Object expected, Object actual, Configuration configuration) { |
||||
if ((expected == null) ^ (actual == null)) { |
||||
return false; |
||||
} |
||||
boolean res = (0 > safeCompare(expected, actual, configuration)); |
||||
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res); |
||||
return res; |
||||
} |
||||
}, |
||||
GTE { |
||||
@Override |
||||
boolean eval(Object expected, Object actual, Configuration configuration) { |
||||
if ((expected == null) ^ (actual == null)) { |
||||
return false; |
||||
} |
||||
boolean res = (0 >= safeCompare(expected, actual, configuration)); |
||||
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res); |
||||
return res; |
||||
} |
||||
}, |
||||
LT { |
||||
@Override |
||||
boolean eval(Object expected, Object actual, Configuration configuration) { |
||||
if ((expected == null) ^ (actual == null)) { |
||||
return false; |
||||
} |
||||
boolean res = (0 < safeCompare(expected, actual, configuration)); |
||||
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res); |
||||
return res; |
||||
} |
||||
}, |
||||
LTE { |
||||
@Override |
||||
boolean eval(Object expected, Object actual, Configuration configuration) { |
||||
if ((expected == null) ^ (actual == null)) { |
||||
return false; |
||||
} |
||||
boolean res = (0 <= safeCompare(expected, actual, configuration)); |
||||
logger.debug("[{}] {} [{}] => {}", actual, name(), expected, res); |
||||
return res; |
||||
} |
||||
}, |
||||
IN { |
||||
@Override |
||||
boolean eval(Object expected, Object actual, Configuration configuration) { |
||||
boolean res = false; |
||||
Collection exps = (Collection) expected; |
||||
for (Object exp : exps) { |
||||
if (0 == safeCompare(exp, actual, configuration)) { |
||||
res = true; |
||||
break; |
||||
} |
||||
} |
||||
logger.debug("[{}] {} [{}] => {}", actual, name(), join(", ", exps), res); |
||||
return res; |
||||
} |
||||
}, |
||||
NIN { |
||||
@Override |
||||
boolean eval(Object expected, Object actual, Configuration configuration) { |
||||
Collection nexps = (Collection) expected; |
||||
boolean res = !nexps.contains(actual); |
||||
logger.debug("[{}] {} [{}] => {}", actual, name(), join(", ", nexps), res); |
||||
return res; |
||||
} |
||||
}, |
||||
ALL { |
||||
@Override |
||||
boolean eval(Object expected, Object actual, Configuration configuration) { |
||||
boolean res = true; |
||||
Collection exps = (Collection) expected; |
||||
if (configuration.getProvider().isArray(actual)) { |
||||
for (Object exp : exps) { |
||||
boolean found = false; |
||||
for (Object check : configuration.getProvider().toIterable(actual)) { |
||||
if (0 == safeCompare(exp, check, configuration)) { |
||||
found = true; |
||||
break; |
||||
} |
||||
} |
||||
if (!found) { |
||||
res = false; |
||||
break; |
||||
} |
||||
} |
||||
logger.debug("[{}] {} [{}] => {}", join(", ", configuration.getProvider().toIterable(actual)), name(), join(", ", exps), res); |
||||
} else { |
||||
res = false; |
||||
logger.debug("[{}] {} [{}] => {}", "<NOT AN ARRAY>", name(), join(", ", exps), res); |
||||
} |
||||
return res; |
||||
} |
||||
}, |
||||
SIZE { |
||||
@Override |
||||
boolean eval(Object expected, Object actual, Configuration configuration) { |
||||
int size = (Integer) expected; |
||||
boolean res; |
||||
if (configuration.getProvider().isArray(actual)) { |
||||
int length = configuration.getProvider().length(actual); |
||||
res = length == size; |
||||
logger.debug("Array with size {} {} {} => {}", length, name(), size, res); |
||||
} else if (actual instanceof String) { |
||||
int length = ((String) actual).length(); |
||||
res = length == size; |
||||
logger.debug("String with length {} {} {} => {}", length, name(), size, res); |
||||
} else { |
||||
res = false; |
||||
logger.debug("{} {} {} => {}", actual == null ? "null" : actual.getClass().getName(), name(), size, res); |
||||
} |
||||
return res; |
||||
} |
||||
}, |
||||
EXISTS { |
||||
@Override |
||||
boolean eval(Object expected, Object actual, Configuration configuration) { |
||||
//This must be handled outside
|
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
}, |
||||
TYPE { |
||||
@Override |
||||
boolean eval(Object expected, Object actual, Configuration configuration) { |
||||
final Class<?> expType = (Class<?>) expected; |
||||
final Class<?> actType = actual == null ? null : actual.getClass(); |
||||
if (actType != null) { |
||||
return expType.isAssignableFrom(actType); |
||||
} |
||||
return false; |
||||
} |
||||
}, |
||||
REGEX { |
||||
@Override |
||||
boolean eval(Object expected, Object actual, Configuration configuration) { |
||||
boolean res = false; |
||||
final Pattern pattern = (Pattern) expected; |
||||
if (actual != null && actual instanceof String) { |
||||
res = pattern.matcher(actual.toString()).matches(); |
||||
} |
||||
logger.debug("[{}] {} [{}] => {}", actual, name(), expected.toString(), res); |
||||
return res; |
||||
} |
||||
}, |
||||
MATCHES { |
||||
@Override |
||||
boolean eval(Object expected, Object actual, Configuration configuration) { |
||||
Predicate exp = (Predicate) expected; |
||||
return exp.apply(actual, configuration); |
||||
} |
||||
}, |
||||
NOT_EMPTY { |
||||
@Override |
||||
boolean eval(Object expected, Object actual, Configuration configuration) { |
||||
boolean res = false; |
||||
if (actual != null) { |
||||
if (configuration.getProvider().isArray(actual)) { |
||||
int len = configuration.getProvider().length(actual); |
||||
res = (0 != len); |
||||
logger.debug("array length = {} {} => {}", len, name(), res); |
||||
} else if (actual instanceof String) { |
||||
int len = ((String) actual).length(); |
||||
res = (0 != len); |
||||
logger.debug("string length = {} {} => {}", len, name(), res); |
||||
} |
||||
} |
||||
return res; |
||||
} |
||||
}; |
||||
|
||||
abstract boolean eval(Object expected, Object actual, Configuration configuration); |
||||
|
||||
public static CriteriaType parse(String str) { |
||||
if ("==".equals(str)) { |
||||
return EQ; |
||||
} else if (">".equals(str)) { |
||||
return GT; |
||||
} else if (">=".equals(str)) { |
||||
return GTE; |
||||
} else if ("<".equals(str)) { |
||||
return LT; |
||||
} else if ("<=".equals(str)) { |
||||
return LTE; |
||||
} else if ("!=".equals(str)) { |
||||
return NE; |
||||
} else { |
||||
throw new UnsupportedOperationException("CriteriaType " + str + " can not be parsed"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private Criteria2(List<Criteria2> criteriaChain, Path path) { |
||||
if (!path.isDefinite()) { |
||||
throw new InvalidCriteriaException("A criteria path must be definite. The path " + path.toString() + " is not!"); |
||||
} |
||||
this.path = path; |
||||
this.criteriaChain = criteriaChain; |
||||
this.criteriaChain.add(this); |
||||
} |
||||
|
||||
private Criteria2(Path path) { |
||||
this(new LinkedList<Criteria2>(), path); |
||||
} |
||||
|
||||
private Criteria2(Path path, CriteriaType criteriaType, Object expected) { |
||||
this(new LinkedList<Criteria2>(), path); |
||||
this.criteriaType = criteriaType; |
||||
this.expected = expected; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public boolean apply(Object model, Configuration configuration) { |
||||
for (Criteria2 criteria : criteriaChain) { |
||||
if (!criteria.eval(model, configuration)) { |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
private boolean eval(Object model, Configuration configuration) { |
||||
if (CriteriaType.EXISTS == criteriaType) { |
||||
boolean exists = ((Boolean) expected); |
||||
try { |
||||
path.evaluate(model, configuration.options(Option.THROW_ON_MISSING_PROPERTY)).get(); |
||||
return exists == true; |
||||
} catch (PathNotFoundException e) { |
||||
return exists == false; |
||||
} |
||||
} else { |
||||
|
||||
try { |
||||
final Object actual = path.evaluate(model, configuration).get(); |
||||
return criteriaType.eval(expected, actual, configuration); |
||||
} catch (CompareException e) { |
||||
return false; |
||||
} catch (PathNotFoundException e) { |
||||
return false; |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Static factory method to create a Criteria using the provided key |
||||
* |
||||
* @param key filed name |
||||
* @return the new criteria |
||||
*/ |
||||
public static Criteria2 where(Path key) { |
||||
return new Criteria2(key); |
||||
} |
||||
|
||||
/** |
||||
* Static factory method to create a Criteria using the provided key |
||||
* |
||||
* @param key filed name |
||||
* @return the new criteria |
||||
*/ |
||||
|
||||
public static Criteria2 where(String key) { |
||||
return where(PathCompiler.tokenize(key)); |
||||
} |
||||
|
||||
/** |
||||
* Static factory method to create a Criteria using the provided key |
||||
* |
||||
* @param key ads new filed to criteria |
||||
* @return the criteria builder |
||||
*/ |
||||
public Criteria2 and(String key) { |
||||
return new Criteria2(this.criteriaChain, PathCompiler.tokenize(key)); |
||||
} |
||||
|
||||
/** |
||||
* Creates a criterion using equality |
||||
* |
||||
* @param o |
||||
* @return |
||||
*/ |
||||
public Criteria2 is(Object o) { |
||||
this.criteriaType = CriteriaType.TYPE.EQ; |
||||
this.expected = o; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Creates a criterion using equality |
||||
* |
||||
* @param o |
||||
* @return |
||||
*/ |
||||
public Criteria2 eq(Object o) { |
||||
return is(o); |
||||
} |
||||
|
||||
/** |
||||
* Creates a criterion using the <b>!=</b> operator |
||||
* |
||||
* @param o |
||||
* @return |
||||
*/ |
||||
public Criteria2 ne(Object o) { |
||||
this.criteriaType = CriteriaType.TYPE.NE; |
||||
this.expected = o; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Creates a criterion using the <b><</b> operator |
||||
* |
||||
* @param o |
||||
* @return |
||||
*/ |
||||
public Criteria2 lt(Object o) { |
||||
this.criteriaType = CriteriaType.TYPE.LT; |
||||
this.expected = o; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Creates a criterion using the <b><=</b> operator |
||||
* |
||||
* @param o |
||||
* @return |
||||
*/ |
||||
public Criteria2 lte(Object o) { |
||||
this.criteriaType = CriteriaType.TYPE.LTE; |
||||
this.expected = o; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Creates a criterion using the <b>></b> operator |
||||
* |
||||
* @param o |
||||
* @return |
||||
*/ |
||||
public Criteria2 gt(Object o) { |
||||
this.criteriaType = CriteriaType.TYPE.GT; |
||||
this.expected = o; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Creates a criterion using the <b>>=</b> operator |
||||
* |
||||
* @param o |
||||
* @return |
||||
*/ |
||||
public Criteria2 gte(Object o) { |
||||
this.criteriaType = CriteriaType.TYPE.GTE; |
||||
this.expected = o; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Creates a criterion using a Regex |
||||
* |
||||
* @param pattern |
||||
* @return |
||||
*/ |
||||
public Criteria2 regex(Pattern pattern) { |
||||
notNull(pattern, "pattern can not be null"); |
||||
this.criteriaType = CriteriaType.TYPE.REGEX; |
||||
this.expected = pattern; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* The <code>in</code> operator is analogous to the SQL IN modifier, allowing you |
||||
* to specify an array of possible matches. |
||||
* |
||||
* @param o the values to match against |
||||
* @return |
||||
*/ |
||||
public Criteria2 in(Object... o) { |
||||
return in(Arrays.asList(o)); |
||||
} |
||||
|
||||
/** |
||||
* The <code>in</code> operator is analogous to the SQL IN modifier, allowing you |
||||
* to specify an array of possible matches. |
||||
* |
||||
* @param c the collection containing the values to match against |
||||
* @return |
||||
*/ |
||||
public Criteria2 in(Collection<?> c) { |
||||
notNull(c, "collection can not be null"); |
||||
this.criteriaType = CriteriaType.TYPE.IN; |
||||
this.expected = c; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* The <code>nin</code> operator is similar to $in except that it selects objects for |
||||
* which the specified field does not have any value in the specified array. |
||||
* |
||||
* @param o the values to match against |
||||
* @return |
||||
*/ |
||||
public Criteria2 nin(Object... o) { |
||||
return nin(Arrays.asList(o)); |
||||
} |
||||
|
||||
/** |
||||
* The <code>nin</code> operator is similar to $in except that it selects objects for |
||||
* which the specified field does not have any value in the specified array. |
||||
* |
||||
* @param c the values to match against |
||||
* @return |
||||
*/ |
||||
public Criteria2 nin(Collection<?> c) { |
||||
notNull(c, "collection can not be null"); |
||||
this.criteriaType = CriteriaType.TYPE.NIN; |
||||
this.expected = c; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* The <code>all</code> operator is similar to $in, but instead of matching any value |
||||
* in the specified array all values in the array must be matched. |
||||
* |
||||
* @param o |
||||
* @return |
||||
*/ |
||||
public Criteria2 all(Object... o) { |
||||
return all(Arrays.asList(o)); |
||||
} |
||||
|
||||
/** |
||||
* The <code>all</code> operator is similar to $in, but instead of matching any value |
||||
* in the specified array all values in the array must be matched. |
||||
* |
||||
* @param c |
||||
* @return |
||||
*/ |
||||
public Criteria2 all(Collection<?> c) { |
||||
notNull(c, "collection can not be null"); |
||||
this.criteriaType = CriteriaType.TYPE.ALL; |
||||
this.expected = c; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* The <code>size</code> operator matches: |
||||
* <p/> |
||||
* <ol> |
||||
* <li>array with the specified number of elements.</li> |
||||
* <li>string with given length.</li> |
||||
* </ol> |
||||
* |
||||
* @param size |
||||
* @return |
||||
*/ |
||||
public Criteria2 size(int size) { |
||||
this.criteriaType = CriteriaType.TYPE.SIZE; |
||||
this.expected = size; |
||||
return this; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Check for existence (or lack thereof) of a field. |
||||
* |
||||
* @param b |
||||
* @return |
||||
*/ |
||||
public Criteria2 exists(boolean b) { |
||||
this.criteriaType = CriteriaType.TYPE.EXISTS; |
||||
this.expected = b; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* The $type operator matches values based on their Java type. |
||||
* |
||||
* @param t |
||||
* @return |
||||
*/ |
||||
public Criteria2 type(Class<?> t) { |
||||
notNull(t, "type can not be null"); |
||||
this.criteriaType = CriteriaType.TYPE.TYPE; |
||||
this.expected = t; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* The <code>notEmpty</code> operator checks that an array or String is not empty. |
||||
* |
||||
* @return |
||||
*/ |
||||
public Criteria2 notEmpty() { |
||||
this.criteriaType = CriteriaType.TYPE.NOT_EMPTY; |
||||
this.expected = null; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* The <code>matches</code> operator checks that an object matches the given predicate. |
||||
* |
||||
* @return |
||||
*/ |
||||
public Criteria2 matches(Predicate p) { |
||||
this.criteriaType = CriteriaType.TYPE.MATCHES; |
||||
this.expected = p; |
||||
return this; |
||||
} |
||||
|
||||
private static int safeCompare(Object expected, Object actual, Configuration configuration) { |
||||
if (expected == null && actual != null) { |
||||
return -1; |
||||
} else if (expected != null && actual == null) { |
||||
return 1; |
||||
} else if (expected == null && actual == null) { |
||||
return 0; |
||||
} else if (expected instanceof String && actual instanceof String) { |
||||
return ((String) expected).compareTo((String) actual); |
||||
} else if (expected instanceof Number && actual instanceof Number) { |
||||
return new BigDecimal(expected.toString()).compareTo(new BigDecimal(actual.toString())); |
||||
} else if (expected instanceof String && actual instanceof Number) { |
||||
return new BigDecimal(expected.toString()).compareTo(new BigDecimal(actual.toString())); |
||||
} else if (expected instanceof String && actual instanceof Boolean) { |
||||
Boolean e = Boolean.valueOf((String)expected); |
||||
Boolean a = (Boolean) actual; |
||||
return e.compareTo(a); |
||||
} else if (expected instanceof Boolean && actual instanceof Boolean) { |
||||
Boolean e = (Boolean) expected; |
||||
Boolean a = (Boolean) actual; |
||||
return e.compareTo(a); |
||||
} else { |
||||
logger.debug("Can not compare a {} with a {}", expected.getClass().getName(), actual.getClass().getName()); |
||||
throw new CompareException(); |
||||
} |
||||
|
||||
} |
||||
|
||||
private static class CompareException extends RuntimeException { |
||||
} |
||||
|
||||
|
||||
public static Criteria2 create(String path, String operator, String expected) { |
||||
if (expected.startsWith("'") && expected.endsWith("'")) { |
||||
expected = expected.substring(1, expected.length() - 1); |
||||
} |
||||
|
||||
Path p = PathCompiler.tokenize(path); |
||||
|
||||
if (operator.isEmpty()) { |
||||
return Criteria2.where(path).exists(true); |
||||
} else { |
||||
return new Criteria2(p, CriteriaType.parse(operator), expected); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
StringBuilder sb = new StringBuilder(); |
||||
sb.append(path.toString()) |
||||
.append("|") |
||||
.append(criteriaType.name()) |
||||
.append("|") |
||||
.append(expected) |
||||
.append("|"); |
||||
return sb.toString(); |
||||
} |
||||
} |
@ -0,0 +1,54 @@
|
||||
package com.jayway.jsonpath; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import static java.util.Arrays.asList; |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
public class Filter2 implements Predicate { |
||||
|
||||
private List<Criteria2> criteriaList = new ArrayList<Criteria2>(); |
||||
|
||||
private Filter2(Criteria2 criteria) { |
||||
this.criteriaList.add(criteria); |
||||
} |
||||
|
||||
private Filter2(List<Criteria2> criteriaList) { |
||||
this.criteriaList = criteriaList; |
||||
} |
||||
|
||||
public static Filter2 filter(Criteria2 criteria) { |
||||
return new Filter2(criteria); |
||||
} |
||||
|
||||
public static Filter2 filter(List<Criteria2> criteriaList) { |
||||
return new Filter2(criteriaList); |
||||
} |
||||
|
||||
@Override |
||||
public boolean apply(Object target, Configuration configuration) { |
||||
for (Criteria2 criteria : criteriaList) { |
||||
if (!criteria.apply(target, configuration)) { |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
public void addCriteria(Criteria2 criteria) { |
||||
criteriaList.add(criteria); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public String toString() { |
||||
StringBuilder sb = new StringBuilder(); |
||||
for (Criteria2 crit : criteriaList) { |
||||
sb.append(crit.toString()); |
||||
} |
||||
return sb.toString(); |
||||
} |
||||
} |
@ -1,27 +0,0 @@
|
||||
/* |
||||
* Copyright 2011 the original author or authors. |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.jayway.jsonpath; |
||||
|
||||
/** |
||||
* @author Kalle Stenflo |
||||
*/ |
||||
@SuppressWarnings("serial") |
||||
public class IndefinitePathException extends RuntimeException { |
||||
|
||||
|
||||
public IndefinitePathException(String path) { |
||||
super("The path " + path + " is not definite"); |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,9 @@
|
||||
package com.jayway.jsonpath; |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
public interface Predicate { |
||||
|
||||
boolean apply(Object target, Configuration configuration); |
||||
} |
@ -0,0 +1,101 @@
|
||||
package com.jayway.jsonpath.internal; |
||||
|
||||
import com.jayway.jsonpath.spi.compiler.Path; |
||||
|
||||
import java.util.Deque; |
||||
import java.util.LinkedList; |
||||
import java.util.Map; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
import java.util.concurrent.locks.ReentrantLock; |
||||
|
||||
public class Cache { |
||||
|
||||
private final ReentrantLock lock = new ReentrantLock(); |
||||
|
||||
|
||||
private final Map<String, Path> map = new ConcurrentHashMap<String, Path>(); |
||||
private final Deque<String> queue = new LinkedList<String>(); |
||||
private final int limit; |
||||
|
||||
|
||||
public Cache(int limit) { |
||||
this.limit = limit; |
||||
} |
||||
|
||||
public void put(String key, Path value) { |
||||
Path oldValue = map.put(key, value); |
||||
if (oldValue != null) { |
||||
removeThenAddKey(key); |
||||
} else { |
||||
addKey(key); |
||||
} |
||||
if (map.size() > limit) { |
||||
map.remove(removeLast()); |
||||
} |
||||
} |
||||
|
||||
public Path get(String key) { |
||||
removeThenAddKey(key); |
||||
return map.get(key); |
||||
} |
||||
|
||||
private void addKey(String key) { |
||||
lock.lock(); |
||||
try { |
||||
queue.addFirst(key); |
||||
} finally { |
||||
lock.unlock(); |
||||
} |
||||
|
||||
|
||||
} |
||||
|
||||
private String removeLast() { |
||||
lock.lock(); |
||||
try { |
||||
final String removedKey = queue.removeLast(); |
||||
return removedKey; |
||||
} finally { |
||||
lock.unlock(); |
||||
} |
||||
} |
||||
|
||||
private void removeThenAddKey(String key) { |
||||
lock.lock(); |
||||
try { |
||||
queue.removeFirstOccurrence(key); |
||||
queue.addFirst(key); |
||||
} finally { |
||||
lock.unlock(); |
||||
} |
||||
|
||||
} |
||||
|
||||
private void removeFirstOccurrence(String key) { |
||||
lock.lock(); |
||||
try { |
||||
queue.removeFirstOccurrence(key); |
||||
} finally { |
||||
lock.unlock(); |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
public Path getSilent(String key) { |
||||
return map.get(key); |
||||
} |
||||
|
||||
public void remove(String key) { |
||||
removeFirstOccurrence(key); |
||||
map.remove(key); |
||||
} |
||||
|
||||
public int size() { |
||||
return map.size(); |
||||
} |
||||
|
||||
public String toString() { |
||||
return map.toString(); |
||||
} |
||||
} |
@ -0,0 +1,159 @@
|
||||
package com.jayway.jsonpath.internal; |
||||
|
||||
import java.util.Arrays; |
||||
|
||||
public class Parser { |
||||
|
||||
public enum Token { |
||||
DOT('.'), |
||||
OPEN_BRACKET('['), |
||||
CLOSE_BRACKET(']'), |
||||
OPEN_PARENTHESIS('('), |
||||
CLOSE_PARENTHESIS(')'), |
||||
BLANK(' '), |
||||
TICK('\''), |
||||
END('^'); |
||||
|
||||
private final char c; |
||||
|
||||
Token(char c) { |
||||
this.c = c; |
||||
} |
||||
} |
||||
|
||||
|
||||
protected String buffer; |
||||
private int i; |
||||
|
||||
public Parser(String src) { |
||||
buffer = src; |
||||
i = -1; |
||||
} |
||||
|
||||
public char prev(){ |
||||
return buffer.charAt(i-1); |
||||
} |
||||
|
||||
public boolean prevIs(char c){ |
||||
if(i <= 0){ |
||||
return false; |
||||
} |
||||
return prev() == c; |
||||
} |
||||
|
||||
public char curr(){ |
||||
return buffer.charAt(i); |
||||
} |
||||
|
||||
public char peek(){ |
||||
return buffer.charAt(i + 1); |
||||
} |
||||
|
||||
public boolean peekIs(Token token){ |
||||
return buffer.charAt(i+1) == token.c; |
||||
} |
||||
|
||||
public char next(){ |
||||
return buffer.charAt(++i); |
||||
} |
||||
|
||||
public boolean hasNext(){ |
||||
return i < buffer.length() - 1; |
||||
} |
||||
|
||||
public String next(int count){ |
||||
//i++;
|
||||
int y = i; |
||||
i = i + count - 1; |
||||
return buffer.substring(y, i+1); |
||||
//return Arrays.copyOfRange(buffer, y, i+1);
|
||||
} |
||||
|
||||
public void trim(Token token){ |
||||
while (peekIs(token)){ |
||||
next(); |
||||
} |
||||
} |
||||
|
||||
public int findOffset(Token... tokens){ |
||||
int y = i; |
||||
char check; |
||||
do { |
||||
if(y == buffer.length()-1 && contains(tokens, Token.END.c)){ |
||||
y++; |
||||
break; |
||||
} |
||||
check = buffer.charAt(++y); |
||||
} while (!contains(tokens, check)); |
||||
|
||||
return y-i; |
||||
} |
||||
|
||||
public String nextUntil(Token... tokens){ |
||||
next(); |
||||
int offset = findOffset(tokens); |
||||
return next(offset); |
||||
} |
||||
|
||||
public boolean isInts(String chars, boolean allowSequence){ |
||||
for (int i = 0; i < chars.length(); i++){ |
||||
|
||||
char c = chars.charAt(i); |
||||
|
||||
boolean isSequenceChar = (c == ' ' || c == ','); |
||||
|
||||
if(!Character.isDigit(c) || (isSequenceChar && allowSequence)){ |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
|
||||
private boolean contains(Token[] arr, char checkFor){ |
||||
for (int i = 0; i < arr.length; i++){ |
||||
if(arr[i].c == checkFor){ |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
|
||||
|
||||
/* |
||||
public Filter2 parse(String filterString){ |
||||
|
||||
char[] chars = filterString.trim().toCharArray(); |
||||
int i = 0; |
||||
|
||||
|
||||
do { |
||||
char current = chars[i]; |
||||
switch (current){ |
||||
case '?': |
||||
break; |
||||
|
||||
case '(': |
||||
break; |
||||
|
||||
case ')': |
||||
break; |
||||
|
||||
case '\''; |
||||
} |
||||
|
||||
|
||||
i++; |
||||
} while (i < chars.length); |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} |
||||
*/ |
||||
} |
@ -0,0 +1,191 @@
|
||||
package com.jayway.jsonpath.internal; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
public class PathFormalizer extends Parser { |
||||
|
||||
private static final Fragment ROOT = new Fragment(Type.ROOT, "$"); |
||||
private static final Fragment SCAN = new Fragment(Type.SCAN, ".."); |
||||
|
||||
|
||||
StringBuilder formalized = new StringBuilder(); |
||||
|
||||
public PathFormalizer(String path) { |
||||
super(path); |
||||
} |
||||
|
||||
|
||||
//$.store.book[?(@.isbn)].isbn
|
||||
//$['store']['book'][?(@['isbn'])]['isbn']
|
||||
|
||||
public String formalize() { |
||||
TokenBuffer buffer = new TokenBuffer(); |
||||
do { |
||||
char current = next(); |
||||
|
||||
switch (current) { |
||||
case '$': |
||||
buffer.append("$").flush(); |
||||
break; |
||||
case '.': |
||||
if (!buffer.isEmpty()) { |
||||
buffer.flush(); |
||||
} |
||||
if (peekIs(Token.DOT)) { |
||||
next(); |
||||
buffer.append("..").flush(); |
||||
} |
||||
break; |
||||
case '[': |
||||
if (!buffer.isEmpty()) { |
||||
buffer.flush(); |
||||
} |
||||
break; |
||||
case ']': |
||||
if (!buffer.isEmpty()) { |
||||
buffer.flush(); |
||||
} |
||||
break; |
||||
case '?': |
||||
if (peekIs(Token.OPEN_PARENTHESIS)) { |
||||
buffer.append("?" + nextUntil(Token.CLOSE_BRACKET)); |
||||
buffer.flush(); |
||||
} |
||||
break; |
||||
default: |
||||
buffer.append(current); |
||||
break; |
||||
} |
||||
} while (hasNext()); |
||||
|
||||
if (!buffer.isEmpty()) { |
||||
buffer.flush(); |
||||
} |
||||
for (Fragment f : buffer.getFragments()) { |
||||
System.out.println("Fragment: " + f.frag + " Type: " + f.type); |
||||
formalized.append(f.toString()); |
||||
} |
||||
return formalized.toString(); |
||||
|
||||
} |
||||
|
||||
private static class TokenBuffer { |
||||
|
||||
private List<Fragment> fragments = new ArrayList<Fragment>(); |
||||
private StringBuilder sb = new StringBuilder(); |
||||
|
||||
public TokenBuffer append(String s) { |
||||
sb.append(s); |
||||
return this; |
||||
} |
||||
|
||||
public TokenBuffer append(char c) { |
||||
sb.append(c); |
||||
return this; |
||||
} |
||||
|
||||
public void flush() { |
||||
fragments.add(new Fragment(Type.PROPERTY, sb.toString().trim())); |
||||
sb = new StringBuilder(); |
||||
} |
||||
|
||||
public boolean isEmpty() { |
||||
return sb.length() == 0; |
||||
} |
||||
|
||||
public List<Fragment> getFragments() { |
||||
return fragments; |
||||
} |
||||
} |
||||
|
||||
private Fragment createFragment(String data) { |
||||
if ("$".equals(data)) { |
||||
return ROOT; |
||||
} else if ("..".equals(data)) { |
||||
return SCAN; |
||||
} else if (isInts(data, true)) { |
||||
return new Fragment(Type.INDEX, new String(data)); |
||||
} else { |
||||
return new Fragment(Type.PROPERTY, new String(data)); |
||||
} |
||||
|
||||
} |
||||
|
||||
private static class Fragment { |
||||
|
||||
|
||||
private Type type; |
||||
private String frag; |
||||
|
||||
private Fragment(Type type, String frag) { |
||||
this.type = type; |
||||
this.frag = frag; |
||||
} |
||||
/* |
||||
//"[-1:]" sliceFrom
|
||||
//"[:1]" sliceTo
|
||||
//"[0:5]" sliceBetween
|
||||
//"[1]"
|
||||
//"[1,2,3]"
|
||||
//"[(@.length - 1)]"
|
||||
*/ |
||||
|
||||
private static Fragment create(String str) { |
||||
|
||||
boolean isProperty = str.startsWith("'") && str.endsWith("'"); |
||||
|
||||
if (isProperty) { |
||||
return new Fragment(Type.PROPERTY, new String(str.substring(1, str.length()-1))); |
||||
} else if ("$".equals(str)) { |
||||
return ROOT; |
||||
} else if ("..".equals(str)) { |
||||
return SCAN; |
||||
} else if ("*".equals(str)) { |
||||
return new Fragment(Type.INDEX, new String(str)); |
||||
} else if ("-1:".equals(str)) { |
||||
return new Fragment(Type.INDEX, new String(str)); |
||||
} else if (":1".equals(str)) { |
||||
return new Fragment(Type.INDEX, new String(str)); |
||||
} else if ("1:2".equals(str)) { |
||||
return new Fragment(Type.INDEX, new String(str)); |
||||
} else if ("1".equals(str)) { |
||||
return new Fragment(Type.INDEX, new String(str)); |
||||
} else if ("1,2,3".equals(str)) { |
||||
return new Fragment(Type.INDEX, new String(str)); |
||||
} else if ("(@.length() - 1)".equals(str)) { |
||||
return new Fragment(Type.INDEX, new String(str)); |
||||
} else if ("?(@.foo == 'bar')".equals(str)) { |
||||
return new Fragment(Type.INDEX, new String(str)); |
||||
} else if ("1,2,3".equals(str)) { |
||||
return new Fragment(Type.INDEX, new String(str)); |
||||
} else { |
||||
return new Fragment(Type.PROPERTY, new String(str)); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return frag; |
||||
} |
||||
} |
||||
|
||||
private static enum Type { |
||||
ROOT, |
||||
SCAN, |
||||
PROPERTY, |
||||
INDEX |
||||
} |
||||
|
||||
public static void main(String[] args) { |
||||
|
||||
String path = "$.store['foo'].arr[10].monkey..book[?(@.isbn)].isbn"; |
||||
System.out.println(path); |
||||
PathFormalizer p = new PathFormalizer(path); |
||||
String f = p.formalize(); |
||||
System.out.println(f); |
||||
|
||||
|
||||
} |
||||
|
||||
} |
@ -1,79 +0,0 @@
|
||||
/* |
||||
* Copyright 2011 the original author or authors. |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.jayway.jsonpath.internal; |
||||
|
||||
import com.jayway.jsonpath.Configuration; |
||||
import com.jayway.jsonpath.InvalidModelException; |
||||
import com.jayway.jsonpath.internal.filter.FilterFactory; |
||||
import com.jayway.jsonpath.internal.filter.PathTokenFilter; |
||||
|
||||
import java.util.regex.Matcher; |
||||
import java.util.regex.Pattern; |
||||
|
||||
/** |
||||
* @author Kalle Stenflo |
||||
*/ |
||||
public class PathToken { |
||||
|
||||
private static final Pattern ARRAY_INDEX_PATTERN = Pattern.compile("\\[(\\d+)]"); |
||||
|
||||
private final String fragment; |
||||
|
||||
private final int tokenIndex; |
||||
|
||||
private final boolean endToken; |
||||
|
||||
public PathToken(String fragment, int tokenIndex, boolean isEndToken) { |
||||
this.fragment = fragment; |
||||
this.tokenIndex = tokenIndex; |
||||
this.endToken = isEndToken; |
||||
} |
||||
|
||||
public PathTokenFilter getFilter(){ |
||||
return FilterFactory.createFilter(this); |
||||
} |
||||
|
||||
public Object filter(Object model, Configuration configuration){ |
||||
return FilterFactory.createFilter(this).filter(model, configuration); |
||||
} |
||||
|
||||
public Object apply(Object model, Configuration configuration){ |
||||
return FilterFactory.createFilter(this).getRef(model, configuration); |
||||
} |
||||
|
||||
public String getFragment() { |
||||
return fragment; |
||||
} |
||||
|
||||
public boolean isRootToken(){ |
||||
return this.tokenIndex == 0; |
||||
} |
||||
|
||||
public boolean isEndToken(){ |
||||
return this.endToken; |
||||
} |
||||
|
||||
public boolean isArrayIndexToken(){ |
||||
return ARRAY_INDEX_PATTERN.matcher(fragment).matches(); |
||||
} |
||||
|
||||
public int getArrayIndex(){ |
||||
Matcher matcher = ARRAY_INDEX_PATTERN.matcher(fragment); |
||||
if(matcher.find()){ |
||||
return Integer.parseInt(matcher.group(1)); |
||||
} |
||||
else throw new InvalidModelException("Could not get array index from fragment " + fragment); |
||||
} |
||||
} |
@ -1,285 +0,0 @@
|
||||
/* |
||||
* Copyright 2011 the original author or authors. |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.jayway.jsonpath.internal; |
||||
|
||||
import com.jayway.jsonpath.InvalidPathException; |
||||
|
||||
import java.util.Iterator; |
||||
import java.util.LinkedList; |
||||
import java.util.List; |
||||
import java.util.regex.Pattern; |
||||
|
||||
/** |
||||
* @author Kalle Stenflo |
||||
*/ |
||||
public class PathTokenizer implements Iterable<PathToken> { |
||||
|
||||
private static Pattern INVALID_PATH_PATTERN = Pattern.compile("[^\\?\\+=\\-\\*/!]\\("); |
||||
|
||||
private List<PathToken> pathTokens = new LinkedList<PathToken>(); |
||||
|
||||
private char[] pathChars; |
||||
private transient int index = 0; |
||||
|
||||
public PathTokenizer(String jsonPath) { |
||||
|
||||
if (INVALID_PATH_PATTERN.matcher(jsonPath).matches()) { |
||||
throw new InvalidPathException("Invalid path: " + jsonPath); |
||||
} |
||||
|
||||
if (!jsonPath.startsWith("$") && !jsonPath.startsWith("$[")) { |
||||
jsonPath = "$." + jsonPath; |
||||
} |
||||
|
||||
this.pathChars = jsonPath.toCharArray(); |
||||
|
||||
|
||||
List<String> tokens = splitPath(); |
||||
int len = tokens.size(); |
||||
int i = 0; |
||||
for (String pathFragment : tokens) { |
||||
pathTokens.add(new PathToken(pathFragment, i, (i==(len-1)) )); |
||||
i++; |
||||
} |
||||
} |
||||
|
||||
public List<String> getFragments() { |
||||
List<String> fragments = new LinkedList<String>(); |
||||
for (PathToken pathToken : pathTokens) { |
||||
fragments.add(pathToken.getFragment()); |
||||
} |
||||
return fragments; |
||||
} |
||||
|
||||
public int size(){ |
||||
return pathTokens.size(); |
||||
} |
||||
|
||||
public String getPath() { |
||||
return new String(pathChars); |
||||
} |
||||
|
||||
public LinkedList<PathToken> getPathTokens(){ |
||||
return new LinkedList<PathToken>(pathTokens); |
||||
} |
||||
|
||||
public Iterator<PathToken> iterator() { |
||||
return pathTokens.iterator(); |
||||
} |
||||
|
||||
public PathToken removeLastPathToken(){ |
||||
PathToken lastPathToken = pathTokens.get(pathTokens.size() - 1); |
||||
|
||||
//TODO: this should also trim the pathChars
|
||||
pathTokens.remove(pathTokens.size() - 1); |
||||
return lastPathToken; |
||||
} |
||||
|
||||
//--------------------------------------------
|
||||
//
|
||||
// Split path
|
||||
//
|
||||
//--------------------------------------------
|
||||
private boolean isEmpty() { |
||||
return index == pathChars.length; |
||||
} |
||||
|
||||
private char peek() { |
||||
return pathChars[index]; |
||||
} |
||||
|
||||
private char poll() { |
||||
char peek = peek(); |
||||
index++; |
||||
return peek; |
||||
} |
||||
|
||||
public List<String> splitPath() { |
||||
|
||||
List<String> fragments = new LinkedList<String>(); |
||||
while (!isEmpty()) { |
||||
skip(' '); |
||||
char current = peek(); |
||||
|
||||
switch (current) { |
||||
case '$': |
||||
fragments.add(Character.toString(current)); |
||||
poll(); |
||||
break; |
||||
|
||||
case '.': |
||||
poll(); |
||||
if (!isEmpty() && peek() == '.') { |
||||
poll(); |
||||
fragments.add(".."); |
||||
|
||||
assertNotInvalidPeek('.'); |
||||
} |
||||
break; |
||||
|
||||
case '[': |
||||
fragments.add(extract(true, ']')); |
||||
break; |
||||
|
||||
default: |
||||
fragments.add(extract(false, '[', '.')); |
||||
} |
||||
} |
||||
return fragments; |
||||
} |
||||
|
||||
|
||||
private String extract(boolean includeSopChar, char... stopChars) { |
||||
|
||||
|
||||
StringBuilder sb = new StringBuilder(); |
||||
while (!isEmpty() && (!isStopChar(peek(), stopChars))) { |
||||
|
||||
if (peek() == '(') { |
||||
do { |
||||
sb.append(poll()); |
||||
} while (peek() != ')'); |
||||
sb.append(poll()); |
||||
} else { |
||||
|
||||
char c = poll(); |
||||
|
||||
if (isStopChar(c, stopChars)) { |
||||
if (includeSopChar) { |
||||
sb.append(c); |
||||
} |
||||
} else { |
||||
sb.append(c); |
||||
} |
||||
} |
||||
} |
||||
if (includeSopChar) { |
||||
assertValidPeek(false, stopChars); |
||||
sb.append(poll()); |
||||
} else { |
||||
assertValidPeek(true, stopChars); |
||||
} |
||||
return clean(sb); |
||||
} |
||||
|
||||
private String clean(StringBuilder sb) { |
||||
|
||||
String src = sb.toString(); |
||||
|
||||
src = trim(src, "'"); |
||||
src = trim(src, ")"); |
||||
src = trim(src, "("); |
||||
src = trimLeft(src, "?"); |
||||
src = trimLeft(src, "@"); |
||||
|
||||
if (src.length() >= 5 && src.subSequence(0, 2).equals("['")) { |
||||
src = src.substring(2); |
||||
src = src.substring(0, src.length() - 2); |
||||
} |
||||
|
||||
return src.trim(); |
||||
} |
||||
|
||||
private String trim(String src, String trim) { |
||||
return trimLeft(trimRight(src, trim), trim); |
||||
} |
||||
|
||||
private String trimRight(String src, String trim) { |
||||
String scanFor = trim + " "; |
||||
if (src.contains(scanFor)) { |
||||
while (src.contains(scanFor)) { |
||||
src = src.replace(scanFor, trim); |
||||
} |
||||
} |
||||
return src; |
||||
} |
||||
|
||||
private String trimLeft(String src, String trim) { |
||||
String scanFor = " " + trim; |
||||
if (src.contains(scanFor)) { |
||||
while (src.contains(scanFor)) { |
||||
src = src.replace(scanFor, trim); |
||||
} |
||||
} |
||||
return src; |
||||
} |
||||
|
||||
private boolean isStopChar(char c, char... scanFor) { |
||||
boolean found = false; |
||||
for (char check : scanFor) { |
||||
if (check == c) { |
||||
found = true; |
||||
break; |
||||
} |
||||
} |
||||
return found; |
||||
} |
||||
|
||||
private void skip(char target) { |
||||
if (isEmpty()) { |
||||
return; |
||||
} |
||||
while (pathChars[index] == target) { |
||||
poll(); |
||||
} |
||||
} |
||||
|
||||
private void assertNotInvalidPeek(char... invalidChars) { |
||||
if (isEmpty()) { |
||||
return; |
||||
} |
||||
char peek = peek(); |
||||
for (char check : invalidChars) { |
||||
if (check == peek) { |
||||
throw new InvalidPathException("Char: " + peek + " at current position is not valid!"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private void assertValidPeek(boolean acceptEmpty, char... validChars) { |
||||
if (isEmpty() && acceptEmpty) { |
||||
return; |
||||
} |
||||
if (isEmpty()) { |
||||
throw new InvalidPathException("Path is incomplete"); |
||||
} |
||||
boolean found = false; |
||||
char peek = peek(); |
||||
for (char check : validChars) { |
||||
if (check == peek) { |
||||
found = true; |
||||
break; |
||||
} |
||||
} |
||||
if (!found) { |
||||
throw new InvalidPathException("Path is invalid"); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
|
||||
StringBuilder sb = new StringBuilder(); |
||||
sb.append("---------------------------------------------------------------------------").append("\n"); |
||||
sb.append("PATH: ").append(getPath()).append("\n"); |
||||
sb.append(String.format("%-50s%-10s%-10s%-10s", "Fragment", "Root", "End", "Array")).append("\n"); |
||||
sb.append("---------------------------------------------------------------------------").append("\n"); |
||||
for (PathToken pathToken : pathTokens) { |
||||
sb.append(String.format("%-50s%-10b%-10b%-10b", pathToken.getFragment(), pathToken.isRootToken(), pathToken.isEndToken(), pathToken.isArrayIndexToken())).append("\n"); |
||||
} |
||||
return sb.toString(); |
||||
|
||||
} |
||||
} |
@ -1,201 +0,0 @@
|
||||
/* |
||||
* Copyright 2011 the original author or authors. |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.jayway.jsonpath.internal.filter; |
||||
|
||||
import com.jayway.jsonpath.Configuration; |
||||
import com.jayway.jsonpath.JsonPath; |
||||
import com.jayway.jsonpath.Option; |
||||
import com.jayway.jsonpath.PathNotFoundException; |
||||
import com.jayway.jsonpath.internal.filter.eval.ExpressionEvaluator; |
||||
import com.jayway.jsonpath.spi.JsonProvider; |
||||
|
||||
import java.util.regex.Matcher; |
||||
import java.util.regex.Pattern; |
||||
|
||||
/** |
||||
* @author Kalle Stenflo |
||||
*/ |
||||
public class ArrayEvalFilter extends PathTokenFilter { |
||||
|
||||
private static final Pattern CONDITION_STATEMENT_PATTERN = Pattern.compile("\\[\\s?\\?\\(.*?[!=<>]+.*?\\)\\s?]"); |
||||
private static final Pattern PATTERN = Pattern.compile("\\s?(@.*?)\\s?([!=<>]+)\\s?(.*?)\\s?"); |
||||
|
||||
|
||||
|
||||
private ConditionStatement[] conditionStatements; |
||||
|
||||
public ArrayEvalFilter(String condition) { |
||||
super(condition); |
||||
|
||||
// [?(@.name == 'Luke Skywalker' && @.occupation == 'Farm boy')]
|
||||
// [?(@.name == 'Luke Skywalker')]
|
||||
|
||||
condition = condition.trim(); |
||||
condition = condition.substring(3, condition.length()-2); |
||||
|
||||
String[] split = condition.split("&&"); |
||||
|
||||
conditionStatements = new ConditionStatement[split.length]; |
||||
for(int i = 0; i < split.length; i++){ |
||||
conditionStatements[i] = createConditionStatement(split[i]); |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
@Override |
||||
public Object filter(Object obj, Configuration configuration) { |
||||
JsonProvider jsonProvider = configuration.getProvider(); |
||||
Iterable<Object> src = null; |
||||
try { |
||||
src = jsonProvider.toIterable(obj); |
||||
} catch (ClassCastException e){ |
||||
throw new PathNotFoundException("The path fragment '" + this.condition + "' can not be applied to a JSON object only a JSON array.", e); |
||||
} |
||||
Object result = jsonProvider.createArray(); |
||||
for (Object item : src) { |
||||
if (isMatch(item, configuration, conditionStatements)) { |
||||
jsonProvider.setProperty(result, jsonProvider.length(result), item); |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
public Object getRef(Object obj, Configuration configuration) { |
||||
throw new UnsupportedOperationException(""); |
||||
} |
||||
|
||||
@Override |
||||
public boolean isArrayFilter() { |
||||
return true; |
||||
} |
||||
|
||||
private boolean isMatch(Object check, Configuration configuration, ConditionStatement... conditionStatements) { |
||||
try { |
||||
for (ConditionStatement conditionStatement : conditionStatements) { |
||||
Object value = conditionStatement.path.read(check, configuration.options(Option.THROW_ON_MISSING_PROPERTY)); |
||||
boolean match = ExpressionEvaluator.eval(value, conditionStatement.getOperator(), conditionStatement.getExpected()); |
||||
if(!match){ |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} catch (PathNotFoundException e){ |
||||
return false; |
||||
} catch (RuntimeException e){ |
||||
throw e; |
||||
} |
||||
} |
||||
|
||||
static boolean isConditionStatement(String condition) { |
||||
return CONDITION_STATEMENT_PATTERN.matcher(condition).matches(); |
||||
} |
||||
|
||||
static ConditionStatement createConditionStatement(String condition) { |
||||
Matcher matcher = PATTERN.matcher(condition); |
||||
if (matcher.matches()) { |
||||
String property = matcher.group(1).trim(); |
||||
String operator = matcher.group(2).trim(); |
||||
String expected = matcher.group(3).trim(); |
||||
|
||||
return new ConditionStatement(condition, property, operator, expected); |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
static class ConditionStatement { |
||||
private final String condition; |
||||
private final String field; |
||||
private final String operator; |
||||
private final String expected; |
||||
private final JsonPath path; |
||||
|
||||
|
||||
ConditionStatement(String condition, String field, String operator, String expected) { |
||||
this.condition = condition; |
||||
this.field = field; |
||||
this.operator = operator; |
||||
|
||||
|
||||
if(expected.startsWith("'")){ |
||||
this.expected = trim(expected, 1, 1); |
||||
}else{ |
||||
this.expected = expected; |
||||
} |
||||
|
||||
if(field.startsWith("@.")){ |
||||
this.path = JsonPath.compile(this.field.replace("@.", "$.")); |
||||
} else { |
||||
this.path = JsonPath.compile(this.field.replace("@", "$")); |
||||
} |
||||
} |
||||
ConditionStatement(String field, String operator, String expected) { |
||||
this(null, field, operator, expected); |
||||
} |
||||
|
||||
String getCondition() { |
||||
return condition; |
||||
} |
||||
|
||||
public JsonPath getJsonPath() { |
||||
return path; |
||||
} |
||||
|
||||
public String getField() { |
||||
return field; |
||||
} |
||||
|
||||
public String getOperator() { |
||||
return operator; |
||||
} |
||||
|
||||
public String getExpected() { |
||||
return expected; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "ConditionStatement{" + |
||||
"field='" + field + '\'' + |
||||
", operator='" + operator + '\'' + |
||||
", expected='" + expected + '\'' + |
||||
'}'; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object o) { |
||||
if (this == o) return true; |
||||
if (o == null || getClass() != o.getClass()) return false; |
||||
|
||||
ConditionStatement that = (ConditionStatement) o; |
||||
|
||||
if (expected != null ? !expected.equals(that.expected) : that.expected != null) return false; |
||||
if (field != null ? !field.equals(that.field) : that.field != null) return false; |
||||
if (operator != null ? !operator.equals(that.operator) : that.operator != null) return false; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
int result = field != null ? field.hashCode() : 0; |
||||
result = 31 * result + (operator != null ? operator.hashCode() : 0); |
||||
result = 31 * result + (expected != null ? expected.hashCode() : 0); |
||||
return result; |
||||
} |
||||
} |
||||
} |
@ -1,155 +0,0 @@
|
||||
/* |
||||
* Copyright 2011 the original author or authors. |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.jayway.jsonpath.internal.filter; |
||||
|
||||
import com.jayway.jsonpath.Configuration; |
||||
import com.jayway.jsonpath.PathNotFoundException; |
||||
import com.jayway.jsonpath.spi.JsonProvider; |
||||
|
||||
import java.util.regex.Pattern; |
||||
|
||||
/** |
||||
* @author Kalle Stenflo |
||||
*/ |
||||
public class ArrayIndexFilter extends PathTokenFilter { |
||||
|
||||
private static final Pattern SINGLE_ARRAY_INDEX_PATTERN = Pattern.compile("\\[\\d+\\]"); |
||||
private static final Pattern COMMA = Pattern.compile(","); |
||||
private static final Pattern SPACE = Pattern.compile(" "); |
||||
private static final String OPERATOR = ":"; |
||||
|
||||
private final String trimmedCondition; |
||||
private boolean usesLenght; |
||||
|
||||
public ArrayIndexFilter(String condition) { |
||||
super(condition); |
||||
|
||||
// remove '[' and ']'
|
||||
String trimmedCondition = trim(condition, 1, 1); |
||||
|
||||
this.usesLenght = trimmedCondition.contains("@.length"); |
||||
|
||||
// resolve '@.length'
|
||||
if(usesLenght){ |
||||
trimmedCondition = trim(trimmedCondition, 1, 1); |
||||
trimmedCondition = trimmedCondition.replace("@.length", ""); |
||||
trimmedCondition = trimmedCondition + OPERATOR; |
||||
} |
||||
this.trimmedCondition = trimmedCondition; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public Object filter(Object obj, Configuration configuration) { |
||||
JsonProvider jsonProvider = configuration.getProvider(); |
||||
|
||||
Object result = jsonProvider.createArray(); |
||||
|
||||
if (trimmedCondition.contains(OPERATOR)) { |
||||
if (trimmedCondition.startsWith(OPERATOR)) { |
||||
String trimmedCondition = trim(this.trimmedCondition, 1, 0); |
||||
int get = Integer.parseInt(trimmedCondition); |
||||
for (int i = 0; i < get; i++) { |
||||
try { |
||||
jsonProvider.setProperty(result, jsonProvider.length(result), jsonProvider.getProperty(obj, i)); |
||||
} catch (IndexOutOfBoundsException e){ |
||||
break; |
||||
} |
||||
} |
||||
return result; |
||||
|
||||
} else if (trimmedCondition.endsWith(OPERATOR)) { |
||||
String trimmedCondition = trim(SPACE.matcher(this.trimmedCondition).replaceAll(""), 0, 1); |
||||
int get = Integer.parseInt(trimmedCondition); |
||||
if(get > 0 || usesLenght){ |
||||
if(get > 0){ |
||||
get = get * -1; |
||||
} |
||||
return jsonProvider.getProperty(obj, jsonProvider.length(obj) + get); |
||||
} else { |
||||
int start = jsonProvider.length(obj) + get; |
||||
int stop = jsonProvider.length(obj); |
||||
|
||||
if(start < 0){ |
||||
start = 0; |
||||
} |
||||
|
||||
for (int i = start; i < stop; i ++){ |
||||
jsonProvider.setProperty(result, jsonProvider.length(result), jsonProvider.getProperty(obj, i)); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
} else { |
||||
String[] indexes = this.trimmedCondition.split(OPERATOR); |
||||
|
||||
int start = Integer.parseInt(indexes[0]); |
||||
int stop = Integer.parseInt(indexes[1]); |
||||
|
||||
for (int i = start; i < stop; i ++){ |
||||
try { |
||||
jsonProvider.setProperty(result, jsonProvider.length(result), jsonProvider.getProperty(obj, i)); |
||||
} catch (IndexOutOfBoundsException e){ |
||||
break; |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
} else { |
||||
String[] indexArr = COMMA.split(trimmedCondition); |
||||
|
||||
//if(obj == null || jsonProvider.length(obj) == 0){
|
||||
if(obj == null){ |
||||
return result; |
||||
} |
||||
|
||||
try { |
||||
if (indexArr.length == 1) { |
||||
/* |
||||
if(jsonProvider.length(obj) == 0){ |
||||
throw new PathNotFoundException("Array index [" + indexArr[0] + "] not found in path"); |
||||
} |
||||
*/ |
||||
|
||||
return jsonProvider.getProperty(obj, indexArr[0]); |
||||
} else { |
||||
for (String idx : indexArr) { |
||||
jsonProvider.setProperty(result, jsonProvider.length(result), jsonProvider.getProperty(obj, idx.trim())); |
||||
} |
||||
return result; |
||||
} |
||||
} catch (IndexOutOfBoundsException e){ |
||||
throw new PathNotFoundException("Array index " + indexArr + " not found in path", e); |
||||
} |
||||
|
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Object getRef(Object obj, Configuration configuration) { |
||||
if(SINGLE_ARRAY_INDEX_PATTERN.matcher(condition).matches()){ |
||||
String trimmedCondition = trim(condition, 1, 1); |
||||
return configuration.getProvider().getProperty(obj, trimmedCondition); |
||||
|
||||
} else { |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public boolean isArrayFilter() { |
||||
return true; |
||||
} |
||||
} |
@ -1,51 +0,0 @@
|
||||
/* |
||||
* Copyright 2011 the original author or authors. |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.jayway.jsonpath.internal.filter; |
||||
|
||||
import com.jayway.jsonpath.Configuration; |
||||
import com.jayway.jsonpath.Filter; |
||||
|
||||
import java.util.LinkedList; |
||||
|
||||
/** |
||||
* @author Kalle Stenflo |
||||
*/ |
||||
public class ArrayQueryFilter extends PathTokenFilter { |
||||
|
||||
ArrayQueryFilter(String condition) { |
||||
super(condition); |
||||
} |
||||
|
||||
@Override |
||||
public Object filter(Object obj, Configuration configuration, LinkedList<Filter> filters, boolean inArrayContext) { |
||||
Filter filter = filters.poll(); |
||||
return filter.doFilter(configuration.getProvider().toIterable(obj), configuration); |
||||
} |
||||
|
||||
@Override |
||||
public Object filter(Object obj, Configuration configuration) { |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
|
||||
@Override |
||||
public Object getRef(Object obj, Configuration configuration) { |
||||
throw new UnsupportedOperationException(""); |
||||
} |
||||
|
||||
@Override |
||||
public boolean isArrayFilter() { |
||||
return true; |
||||
} |
||||
} |
@ -1,136 +0,0 @@
|
||||
/* |
||||
* Copyright 2011 the original author or authors. |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.jayway.jsonpath.internal.filter; |
||||
|
||||
import com.jayway.jsonpath.Configuration; |
||||
import com.jayway.jsonpath.Filter; |
||||
import com.jayway.jsonpath.Option; |
||||
import com.jayway.jsonpath.PathNotFoundException; |
||||
import com.jayway.jsonpath.internal.PathToken; |
||||
import com.jayway.jsonpath.spi.JsonProvider; |
||||
|
||||
import java.util.Collection; |
||||
import java.util.LinkedList; |
||||
|
||||
/** |
||||
* @author Kalle Stenflo |
||||
*/ |
||||
public class FieldFilter extends PathTokenFilter { |
||||
|
||||
private final String[] split; |
||||
private final PathToken pathToken; |
||||
|
||||
public FieldFilter(PathToken pathToken) { |
||||
super(pathToken.getFragment()); |
||||
this.pathToken = pathToken; |
||||
this.split = condition.split("','"); |
||||
} |
||||
|
||||
@Override |
||||
public Object filter(Object obj, Configuration configuration, LinkedList<Filter> filters, boolean inArrayContext) { |
||||
JsonProvider jsonProvider = configuration.getProvider(); |
||||
if (jsonProvider.isArray(obj)) { |
||||
if (!inArrayContext) { |
||||
throw new PathNotFoundException("Path '" + condition + "' is being applied to an array. Arrays can not have attributes."); |
||||
} else { |
||||
Object result = jsonProvider.createArray(); |
||||
for (Object current : jsonProvider.toIterable(obj)) { |
||||
if (jsonProvider.isMap(current)) { |
||||
|
||||
Collection<String> keys = jsonProvider.getPropertyKeys(current); |
||||
|
||||
if(split.length == 1){ |
||||
if (keys.contains(condition)) { |
||||
Object o = jsonProvider.getProperty(current, condition); |
||||
|
||||
boolean isArr = jsonProvider.isArray(o); |
||||
boolean isEnd = pathToken.isEndToken(); |
||||
if (isArr && !isEnd) { |
||||
for(Object item : jsonProvider.toIterable(o)){ |
||||
jsonProvider.setProperty(result, jsonProvider.length(result), item); |
||||
} |
||||
} else { |
||||
jsonProvider.setProperty(result, jsonProvider.length(result), jsonProvider.getProperty(current, condition)); |
||||
} |
||||
} |
||||
} else { |
||||
Object res = jsonProvider.createMap(); |
||||
for (String prop : split) { |
||||
if (keys.contains(prop)) { |
||||
jsonProvider.setProperty(res, prop, jsonProvider.getProperty(current, prop)); |
||||
} |
||||
} |
||||
jsonProvider.setProperty(result, jsonProvider.length(result), res); |
||||
} |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
} else if (jsonProvider.isMap(obj)){ |
||||
|
||||
Collection<String> keys = jsonProvider.getPropertyKeys(obj); |
||||
if(!keys.contains(condition) && split.length == 1){ |
||||
|
||||
if(configuration.getOptions().contains(Option.THROW_ON_MISSING_PROPERTY)){ |
||||
throw new PathNotFoundException("Path '" + condition + "' not found in the current context:\n" + jsonProvider.toJson(obj)); |
||||
} |
||||
|
||||
if(pathToken.isEndToken()){ |
||||
return null; |
||||
} else { |
||||
throw new PathNotFoundException("Path '" + condition + "' not found in the current context:\n" + jsonProvider.toJson(obj)); |
||||
} |
||||
} else { |
||||
|
||||
if(split.length == 1){ |
||||
return jsonProvider.getProperty(obj, condition); |
||||
} else { |
||||
Object res = jsonProvider.createMap(); |
||||
for (String prop : split) { |
||||
if(keys.contains(prop)){ |
||||
jsonProvider.setProperty(res, prop, jsonProvider.getProperty(obj, prop)); |
||||
} |
||||
} |
||||
return res; |
||||
} |
||||
} |
||||
} else { |
||||
throw new PathNotFoundException("Failed to access property: '" + condition + "' on object " + obj); |
||||
} |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public Object filter(Object obj, Configuration configuration) { |
||||
JsonProvider jsonProvider = configuration.getProvider(); |
||||
if (jsonProvider.isArray(obj)) { |
||||
return obj; |
||||
} else { |
||||
return jsonProvider.getProperty(obj, condition); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Object getRef(Object obj, Configuration configuration) { |
||||
return filter(obj, configuration); |
||||
} |
||||
|
||||
@Override |
||||
public boolean isArrayFilter() { |
||||
return false; |
||||
} |
||||
|
||||
|
||||
} |
@ -1,85 +0,0 @@
|
||||
/* |
||||
* Copyright 2011 the original author or authors. |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.jayway.jsonpath.internal.filter; |
||||
|
||||
import com.jayway.jsonpath.InvalidPathException; |
||||
import com.jayway.jsonpath.internal.PathToken; |
||||
|
||||
/** |
||||
* @author Kalle Stenflo |
||||
*/ |
||||
public class FilterFactory { |
||||
|
||||
private final static PathTokenFilter DOCUMENT_FILTER = new PassthroughFilter("$", false); |
||||
private final static PathTokenFilter ALL_ARRAY_ITEMS_FILTER = new PassthroughFilter("[*]", true); |
||||
private final static PathTokenFilter WILDCARD_FILTER = new WildcardFilter("*"); |
||||
private final static PathTokenFilter SCAN_FILTER = new ScanFilter(".."); |
||||
private final static PathTokenFilter ARRAY_QUERY_FILTER = new ArrayQueryFilter("[?]"); |
||||
|
||||
public static PathTokenFilter createFilter(PathToken token) { |
||||
|
||||
String pathFragment = token.getFragment(); |
||||
|
||||
if (DOCUMENT_FILTER.getCondition().equals(pathFragment) && token.isRootToken()) { //"$"
|
||||
|
||||
return DOCUMENT_FILTER; |
||||
|
||||
} else if (ALL_ARRAY_ITEMS_FILTER.getCondition().equals(pathFragment)) { //"[*]"
|
||||
|
||||
return ALL_ARRAY_ITEMS_FILTER; |
||||
|
||||
} else if ("*".equals(pathFragment)) { |
||||
|
||||
return WILDCARD_FILTER; |
||||
|
||||
} else if (SCAN_FILTER.getCondition().equals(pathFragment)) { |
||||
|
||||
return SCAN_FILTER; |
||||
|
||||
} else if (ARRAY_QUERY_FILTER.getCondition().equals(pathFragment)) { //"[?]"
|
||||
|
||||
return ARRAY_QUERY_FILTER; |
||||
|
||||
} else if (!pathFragment.contains("[")) { |
||||
|
||||
return new FieldFilter(token); |
||||
|
||||
} else if (pathFragment.contains("[")) { |
||||
|
||||
if (pathFragment.startsWith("[?")) { |
||||
|
||||
if(ArrayEvalFilter.isConditionStatement(pathFragment)){ |
||||
return new ArrayEvalFilter(pathFragment); |
||||
} else if (!pathFragment.contains("=") && !pathFragment.contains("<") && !pathFragment.contains(">")) { |
||||
//[?(@.isbn)]
|
||||
return new HasPathFilter(pathFragment); |
||||
} else { |
||||
throw new InvalidPathException("Failed to create PathTokenFilter for path fragment: " + pathFragment); |
||||
} |
||||
} else { |
||||
//[0]
|
||||
//[0,1, ...]
|
||||
//[-1:]
|
||||
//[:1]
|
||||
return new ArrayIndexFilter(pathFragment); |
||||
} |
||||
} |
||||
|
||||
throw new UnsupportedOperationException("can not find filter for path fragment " + pathFragment); |
||||
|
||||
} |
||||
|
||||
|
||||
} |
@ -1,72 +0,0 @@
|
||||
/* |
||||
* Copyright 2011 the original author or authors. |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.jayway.jsonpath.internal.filter; |
||||
|
||||
import com.jayway.jsonpath.Configuration; |
||||
import com.jayway.jsonpath.JsonPath; |
||||
import com.jayway.jsonpath.Option; |
||||
import com.jayway.jsonpath.PathNotFoundException; |
||||
import com.jayway.jsonpath.spi.JsonProvider; |
||||
|
||||
/** |
||||
* @author Kalle Stenflo |
||||
*/ |
||||
public class HasPathFilter extends PathTokenFilter { |
||||
|
||||
private final JsonPath path; |
||||
|
||||
public HasPathFilter(String condition) { |
||||
super(condition); |
||||
String trimmedCondition = condition; |
||||
|
||||
if(condition.contains("['")){ |
||||
trimmedCondition = trimmedCondition.replace("['", "."); |
||||
trimmedCondition = trimmedCondition.replace("']", ""); |
||||
} |
||||
|
||||
this.path = JsonPath.compile(trim(trimmedCondition, 5, 2)); |
||||
} |
||||
|
||||
@Override |
||||
public Object filter(Object obj, Configuration configuration) { |
||||
JsonProvider jsonProvider = configuration.getProvider(); |
||||
|
||||
//[?(@.isbn)]
|
||||
Iterable<Object> src = jsonProvider.toIterable(obj); |
||||
Object result = jsonProvider.createArray(); |
||||
|
||||
for (Object item : src) { |
||||
if(jsonProvider.isMap(item)){ |
||||
try{ |
||||
path.read(item, Configuration.builder().options(Option.THROW_ON_MISSING_PROPERTY).jsonProvider(jsonProvider).build()); |
||||
jsonProvider.setProperty(result, jsonProvider.length(result), item); |
||||
} catch (PathNotFoundException e){ |
||||
// the path was not found in the item
|
||||
} |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
public Object getRef(Object obj, Configuration configuration) { |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean isArrayFilter() { |
||||
return true; |
||||
} |
||||
} |
@ -1,46 +0,0 @@
|
||||
/* |
||||
* Copyright 2011 the original author or authors. |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.jayway.jsonpath.internal.filter; |
||||
|
||||
import com.jayway.jsonpath.Configuration; |
||||
|
||||
/** |
||||
* @author Kalle Stenflo |
||||
*/ |
||||
public class PassthroughFilter extends PathTokenFilter { |
||||
|
||||
|
||||
private boolean isArrayFilter; |
||||
|
||||
public PassthroughFilter(String condition, boolean isArrayFilter) { |
||||
super(condition); |
||||
this.isArrayFilter = isArrayFilter; |
||||
} |
||||
|
||||
@Override |
||||
public Object filter(Object obj, Configuration configuration) { |
||||
return obj; |
||||
} |
||||
|
||||
@Override |
||||
public Object getRef(Object obj, Configuration configuration) { |
||||
return obj; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isArrayFilter() { |
||||
return isArrayFilter; |
||||
} |
||||
} |
@ -1,70 +0,0 @@
|
||||
/* |
||||
* Copyright 2011 the original author or authors. |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.jayway.jsonpath.internal.filter; |
||||
|
||||
import com.jayway.jsonpath.Configuration; |
||||
import com.jayway.jsonpath.Filter; |
||||
|
||||
import java.util.LinkedList; |
||||
|
||||
/** |
||||
* @author Kalle Stenflo |
||||
*/ |
||||
public abstract class PathTokenFilter { |
||||
|
||||
final String condition; |
||||
|
||||
PathTokenFilter(String condition) { |
||||
this.condition = condition; |
||||
} |
||||
|
||||
String getCondition() { |
||||
return condition; |
||||
} |
||||
|
||||
static String trim(String str, int front, int end) { |
||||
String res = str; |
||||
|
||||
if (front > 0) { |
||||
res = str.substring(front); |
||||
} |
||||
if (end > 0) { |
||||
res = res.substring(0, res.length() - end); |
||||
} |
||||
return res; |
||||
} |
||||
|
||||
public Object filter(Object obj, Configuration configuration, LinkedList<Filter> filters, boolean inArrayContext){ |
||||
return filter(obj, configuration); |
||||
} |
||||
|
||||
public abstract Object filter(Object obj, Configuration configuration); |
||||
|
||||
public abstract Object getRef(Object obj, Configuration configuration); |
||||
|
||||
public abstract boolean isArrayFilter(); |
||||
|
||||
@Override |
||||
public String toString() { |
||||
|
||||
if(isArrayFilter()){ |
||||
return getClass().getSimpleName() + " => " + condition; |
||||
} else { |
||||
return getClass().getSimpleName() + " => '" + condition + "'"; |
||||
} |
||||
|
||||
|
||||
} |
||||
} |
@ -1,62 +0,0 @@
|
||||
/* |
||||
* Copyright 2011 the original author or authors. |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.jayway.jsonpath.internal.filter; |
||||
|
||||
|
||||
import com.jayway.jsonpath.Configuration; |
||||
import com.jayway.jsonpath.spi.JsonProvider; |
||||
|
||||
/** |
||||
* @author Kalle Stenflo |
||||
*/ |
||||
public class ScanFilter extends PathTokenFilter { |
||||
|
||||
public ScanFilter(String condition) { |
||||
super(condition); |
||||
} |
||||
|
||||
@Override |
||||
public Object filter(Object obj, Configuration configuration) { |
||||
JsonProvider jsonProvider = configuration.getProvider(); |
||||
Object result = jsonProvider.createArray(); |
||||
scan(obj, result, jsonProvider); |
||||
|
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isArrayFilter() { |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public Object getRef(Object obj, Configuration configuration) { |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
|
||||
|
||||
private void scan(Object container, Object result, JsonProvider jsonProvider) { |
||||
|
||||
if (jsonProvider.isMap(container)) { |
||||
jsonProvider.setProperty(result, jsonProvider.length(result), container); |
||||
} |
||||
|
||||
for (Object value : jsonProvider.toIterable(container)) { |
||||
if (jsonProvider.isContainer(value)) { |
||||
scan(value, result, jsonProvider); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,57 +0,0 @@
|
||||
/* |
||||
* Copyright 2011 the original author or authors. |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.jayway.jsonpath.internal.filter; |
||||
|
||||
import com.jayway.jsonpath.Configuration; |
||||
import com.jayway.jsonpath.spi.JsonProvider; |
||||
|
||||
/** |
||||
* @author Kalle Stenflo |
||||
*/ |
||||
public class WildcardFilter extends PathTokenFilter { |
||||
|
||||
public WildcardFilter(String condition) { |
||||
super(condition); |
||||
} |
||||
|
||||
@Override |
||||
public Object filter(Object obj, Configuration configuration) { |
||||
JsonProvider jsonProvider = configuration.getProvider(); |
||||
Object result = jsonProvider.createArray(); |
||||
|
||||
if (jsonProvider.isArray(obj)) { |
||||
for (Object current : jsonProvider.toIterable(obj)) { |
||||
for (Object value : jsonProvider.toIterable(current)) { |
||||
jsonProvider.setProperty(result, jsonProvider.length(result), value); |
||||
} |
||||
} |
||||
} else { |
||||
for (Object value : jsonProvider.toIterable(obj)) { |
||||
jsonProvider.setProperty(result, jsonProvider.length(result), value); |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
public Object getRef(Object obj, Configuration configuration) { |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean isArrayFilter() { |
||||
return true; |
||||
} |
||||
} |
@ -1,243 +0,0 @@
|
||||
/* |
||||
* Copyright 2011 the original author or authors. |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.jayway.jsonpath.internal.filter.eval; |
||||
|
||||
import java.math.BigDecimal; |
||||
import java.math.BigInteger; |
||||
import java.util.Collections; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* @author Kalle Stenflo |
||||
*/ |
||||
public class ExpressionEvaluator { |
||||
|
||||
private static final String NULL_VALUE = "null"; |
||||
|
||||
public enum Operator { |
||||
equal("=="), |
||||
not_equal("!="), |
||||
less_or_greater_than("<>"), //same as not_equal
|
||||
greater_than(">"), |
||||
greater_than_or_equal(">="), |
||||
less_than("<"), |
||||
less_than_or_equal("<="); |
||||
|
||||
private final String representation; |
||||
|
||||
private Operator(String representation) { |
||||
this.representation = representation; |
||||
} |
||||
|
||||
public String getRepresentation() { |
||||
return representation; |
||||
} |
||||
} |
||||
|
||||
private static Map<String, Operator> operatorsByRepresentation; |
||||
|
||||
static { |
||||
Map<String, Operator> map = new HashMap<String, Operator>(); |
||||
for (Operator op : Operator.values()) { |
||||
map.put(op.getRepresentation(), op); |
||||
} |
||||
ExpressionEvaluator.operatorsByRepresentation = Collections.unmodifiableMap(map); |
||||
} |
||||
|
||||
|
||||
public static <T> boolean eval(T actual, String comparator, String expected) { |
||||
|
||||
Operator operator = operatorsByRepresentation.get(comparator); |
||||
if (operator == null) { |
||||
throw new IllegalArgumentException("Unsupported operator " + comparator); |
||||
} |
||||
|
||||
if(actual == null){ |
||||
if(operator == Operator.equal){ |
||||
return NULL_VALUE.equals(expected); |
||||
} else if(operator == Operator.not_equal || operator == Operator.less_or_greater_than){ |
||||
return !NULL_VALUE.equals(expected); |
||||
} |
||||
} else { |
||||
if(operator == Operator.not_equal || operator == Operator.less_or_greater_than){ |
||||
if(NULL_VALUE.equals(expected)){ |
||||
return true; |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
if (actual instanceof Long) { |
||||
|
||||
Long a = (Long) actual; |
||||
Long e = Long.parseLong(expected.trim()); |
||||
switch (operator) { |
||||
case equal: |
||||
return a.longValue() == e.longValue(); |
||||
case not_equal: |
||||
case less_or_greater_than: |
||||
return a.longValue() != e.longValue(); |
||||
case greater_than: |
||||
return a > e; |
||||
case greater_than_or_equal: |
||||
return a >= e; |
||||
case less_than: |
||||
return a < e; |
||||
case less_than_or_equal: |
||||
return a <= e; |
||||
default: |
||||
throw new UnsupportedOperationException("Cannot handle operator " + operator); |
||||
} |
||||
|
||||
|
||||
} else if (actual instanceof Integer) { |
||||
Integer a = (Integer) actual; |
||||
Integer e = Integer.parseInt(expected.trim()); |
||||
|
||||
switch (operator) { |
||||
case equal: |
||||
return a.intValue() == e.intValue(); |
||||
case not_equal: |
||||
case less_or_greater_than: |
||||
return a.intValue() != e.intValue(); |
||||
case greater_than: |
||||
return a > e; |
||||
case greater_than_or_equal: |
||||
return a >= e; |
||||
case less_than: |
||||
return a < e; |
||||
case less_than_or_equal: |
||||
return a <= e; |
||||
default: |
||||
throw new UnsupportedOperationException("Cannot handle operator " + operator); |
||||
} |
||||
} else if (actual instanceof Double) { |
||||
|
||||
Double a = (Double) actual; |
||||
Double e = Double.parseDouble(expected.trim()); |
||||
|
||||
switch (operator) { |
||||
case equal: |
||||
return a.doubleValue() == e.doubleValue(); |
||||
case not_equal: |
||||
case less_or_greater_than: |
||||
return a.doubleValue() != e.doubleValue(); |
||||
case greater_than: |
||||
return a > e; |
||||
case greater_than_or_equal: |
||||
return a >= e; |
||||
case less_than: |
||||
return a < e; |
||||
case less_than_or_equal: |
||||
return a <= e; |
||||
default: |
||||
throw new UnsupportedOperationException("Cannot handle operator " + operator); |
||||
} |
||||
} else if (actual instanceof String) { |
||||
|
||||
switch (operator) { |
||||
case greater_than: |
||||
case greater_than_or_equal: |
||||
case less_than: |
||||
case less_than_or_equal: |
||||
// we might want to throw an exception here
|
||||
return false; |
||||
case equal: |
||||
case not_equal: |
||||
case less_or_greater_than: |
||||
String a = (String) actual; |
||||
String expectedTrimmed = expected.trim(); |
||||
if (expectedTrimmed.startsWith("'")) { |
||||
expectedTrimmed = expectedTrimmed.substring(1); |
||||
} |
||||
if (expectedTrimmed.endsWith("'")) { |
||||
expectedTrimmed = expectedTrimmed.substring(0, expected.length() - 1); |
||||
} |
||||
|
||||
if (operator == Operator.equal) { |
||||
return a.equals(expectedTrimmed); |
||||
} else if (operator == Operator.not_equal || operator == Operator.less_or_greater_than) { |
||||
return !a.equals(expectedTrimmed); |
||||
} |
||||
default: |
||||
throw new UnsupportedOperationException("Cannot handle operator " + operator); |
||||
} |
||||
} else if (actual instanceof Boolean) { |
||||
switch (operator) { |
||||
case equal: |
||||
case not_equal: |
||||
case less_or_greater_than: |
||||
Boolean a = (Boolean) actual; |
||||
Boolean e = Boolean.valueOf(expected); |
||||
if (operator == Operator.equal) { |
||||
return a.equals(e); |
||||
} else if (operator == Operator.not_equal || operator == Operator.less_or_greater_than) { |
||||
return !a.equals(e); |
||||
} |
||||
default: |
||||
throw new UnsupportedOperationException("Cannot handle operator " + operator); |
||||
} |
||||
} else if (actual instanceof BigInteger) { |
||||
|
||||
BigInteger a = (BigInteger) actual; |
||||
BigInteger e = new BigInteger(expected.trim()); |
||||
|
||||
switch (operator) { |
||||
case equal: |
||||
return a.compareTo(e) == 0; |
||||
case not_equal: |
||||
return a.compareTo(e) != 0; |
||||
case less_or_greater_than: |
||||
return a.compareTo(e) != 0; |
||||
case greater_than: |
||||
return a.compareTo(e) > 0; |
||||
case greater_than_or_equal: |
||||
return a.compareTo(e) >= 0; |
||||
case less_than: |
||||
return a.compareTo(e) < 0; |
||||
case less_than_or_equal: |
||||
return a.compareTo(e) <= 0; |
||||
default: |
||||
throw new UnsupportedOperationException("Cannot handle operator " + operator); |
||||
} |
||||
} else if (actual instanceof BigDecimal) { |
||||
|
||||
BigDecimal a = (BigDecimal) actual; |
||||
BigDecimal e = new BigDecimal(expected); |
||||
|
||||
switch (operator) { |
||||
case equal: |
||||
return a.compareTo(e) == 0; |
||||
case not_equal: |
||||
return a.compareTo(e) != 0; |
||||
case less_or_greater_than: |
||||
return a.compareTo(e) != 0; |
||||
case greater_than: |
||||
return a.compareTo(e) > 0; |
||||
case greater_than_or_equal: |
||||
return a.compareTo(e) >= 0; |
||||
case less_than: |
||||
return a.compareTo(e) < 0; |
||||
case less_than_or_equal: |
||||
return a.compareTo(e) <= 0; |
||||
default: |
||||
throw new UnsupportedOperationException("Cannot handle operator " + operator); |
||||
} |
||||
|
||||
} |
||||
return false; |
||||
} |
||||
} |
@ -0,0 +1,155 @@
|
||||
package com.jayway.jsonpath.internal.spi.compiler; |
||||
|
||||
import com.jayway.jsonpath.InvalidPathException; |
||||
import com.jayway.jsonpath.PathNotFoundException; |
||||
import com.jayway.jsonpath.internal.Utils; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
import java.util.List; |
||||
|
||||
import static java.lang.String.format; |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
class ArrayPathToken extends PathToken { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ArrayPathToken.class); |
||||
|
||||
public static enum Operation { |
||||
CONTEXT_SIZE, |
||||
SLICE_TO, |
||||
SLICE_FROM, |
||||
SLICE_BETWEEN, |
||||
INDEX_SEQUENCE, |
||||
SINGLE_INDEX; |
||||
} |
||||
|
||||
private final List<Integer> criteria; |
||||
private final Operation operation; |
||||
private final boolean isDefinite; |
||||
|
||||
public ArrayPathToken(List<Integer> criteria, Operation operation) { |
||||
this.criteria = criteria; |
||||
this.operation = operation; |
||||
this.isDefinite = (Operation.SINGLE_INDEX == operation || Operation.CONTEXT_SIZE == operation); |
||||
} |
||||
|
||||
@Override |
||||
void evaluate(String currentPath, Object model, EvaluationContextImpl ctx) { |
||||
if (!ctx.jsonProvider().isArray(model)) { |
||||
throw new InvalidPathException(format("Filter: %s can only be applied to arrays. Current context is: %s", toString(), model)); |
||||
} |
||||
|
||||
try { |
||||
if (operation == Operation.SINGLE_INDEX) { |
||||
handleArrayIndex(criteria.get(0), currentPath, model, ctx); |
||||
} else if (operation == Operation.INDEX_SEQUENCE) { |
||||
for (Integer idx : criteria) { |
||||
handleArrayIndex(criteria.get(idx), currentPath, model, ctx); |
||||
} |
||||
} else if (Operation.CONTEXT_SIZE == operation) { |
||||
int length = ctx.jsonProvider().length(model); |
||||
int idx = length + criteria.get(0); |
||||
handleArrayIndex(idx, currentPath, model, ctx); |
||||
} |
||||
//[2:]
|
||||
else if (Operation.SLICE_FROM == operation) { |
||||
int input = criteria.get(0); |
||||
int length = ctx.jsonProvider().length(model); |
||||
int from = input; |
||||
if (from < 0) { |
||||
//calculate slice start from array length
|
||||
from = length + from; |
||||
} |
||||
from = Math.max(0, from); |
||||
|
||||
logger.debug("Slice from index on array with length: {}. From index: {} to: {}. Input: {}", length, from, length - 1, toString()); |
||||
|
||||
if (length == 0 || from >= length) { |
||||
return; |
||||
} |
||||
for (int i = from; i < length; i++) { |
||||
handleArrayIndex(i, currentPath, model, ctx); |
||||
} |
||||
} |
||||
//[:2]
|
||||
else if (Operation.SLICE_TO == operation) { |
||||
int input = criteria.get(0); |
||||
int length = ctx.jsonProvider().length(model); |
||||
int to = input; |
||||
if (to < 0) { |
||||
//calculate slice end from array length
|
||||
to = length + to; |
||||
} |
||||
to = Math.min(length, to); |
||||
|
||||
logger.debug("Slice to index on array with length: {}. From index: 0 to: {}. Input: {}", length, to, toString()); |
||||
|
||||
if (length == 0) { |
||||
return; |
||||
} |
||||
for (int i = 0; i < to; i++) { |
||||
handleArrayIndex(i, currentPath, model, ctx); |
||||
} |
||||
} |
||||
//[2:4]
|
||||
else if (Operation.SLICE_BETWEEN == operation) { |
||||
int from = criteria.get(0); |
||||
int to = criteria.get(1); |
||||
int length = ctx.jsonProvider().length(model); |
||||
|
||||
to = Math.min(length, to); |
||||
|
||||
if (from >= to || length == 0) { |
||||
return; |
||||
} |
||||
|
||||
logger.debug("Slice between indexes on array with length: {}. From index: {} to: {}. Input: {}", length, from, to, toString()); |
||||
|
||||
for (int i = from; i < to; i++) { |
||||
handleArrayIndex(i, currentPath, model, ctx); |
||||
} |
||||
} |
||||
} catch (IndexOutOfBoundsException e) { |
||||
throw new PathNotFoundException(e); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public String getPathFragment() { |
||||
StringBuilder sb = new StringBuilder(); |
||||
if (Operation.SINGLE_INDEX == operation || Operation.INDEX_SEQUENCE == operation) { |
||||
sb.append("[") |
||||
.append(Utils.join(",", "", criteria)) |
||||
.append("]"); |
||||
} else if (Operation.CONTEXT_SIZE == operation) { |
||||
sb.append("[@.size()") |
||||
.append(criteria.get(0)) |
||||
.append("]"); |
||||
} else if (Operation.SLICE_FROM == operation) { |
||||
sb.append("[") |
||||
.append(criteria.get(0)) |
||||
.append(":]"); |
||||
} else if (Operation.SLICE_TO == operation) { |
||||
sb.append("[:") |
||||
.append(criteria.get(0)) |
||||
.append("]"); |
||||
} else if (Operation.SLICE_BETWEEN == operation) { |
||||
sb.append("[") |
||||
.append(criteria.get(0)) |
||||
.append(":") |
||||
.append(criteria.get(1)) |
||||
.append("]"); |
||||
} else |
||||
sb.append("NOT IMPLEMENTED"); |
||||
|
||||
return sb.toString(); |
||||
} |
||||
|
||||
@Override |
||||
boolean isTokenDefinite() { |
||||
return isDefinite; |
||||
} |
||||
} |
@ -0,0 +1,51 @@
|
||||
package com.jayway.jsonpath.internal.spi.compiler; |
||||
|
||||
import com.jayway.jsonpath.Configuration; |
||||
import com.jayway.jsonpath.internal.JsonFormatter; |
||||
import com.jayway.jsonpath.spi.compiler.EvaluationContext; |
||||
import com.jayway.jsonpath.spi.compiler.Path; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
class CompiledPath implements Path { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CompiledPath.class); |
||||
|
||||
private final PathToken root; |
||||
|
||||
|
||||
public CompiledPath(PathToken root) { |
||||
this.root = root; |
||||
} |
||||
|
||||
@Override |
||||
public EvaluationContext evaluate(Object model, Configuration configuration) { |
||||
if(logger.isDebugEnabled()) { |
||||
logger.debug("Evaluating path: {}", toString()); |
||||
} |
||||
|
||||
EvaluationContextImpl ctx = new EvaluationContextImpl(configuration, isDefinite()); |
||||
root.evaluate("", model, ctx); |
||||
|
||||
if(logger.isDebugEnabled()) { |
||||
logger.debug("Found:\n{}", JsonFormatter.prettyPrint(ctx.configuration().getProvider().toJson(ctx.getPathList()))); |
||||
} |
||||
|
||||
return ctx; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isDefinite() { |
||||
return root.isPathDefinite(); |
||||
} |
||||
|
||||
@Override |
||||
public int tokenCount() { |
||||
return root.getTokenCount(); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return root.toString(); |
||||
} |
||||
} |
@ -0,0 +1,63 @@
|
||||
package com.jayway.jsonpath.internal.spi.compiler; |
||||
|
||||
import com.jayway.jsonpath.Configuration; |
||||
import com.jayway.jsonpath.Option; |
||||
import com.jayway.jsonpath.spi.json.JsonProvider; |
||||
import com.jayway.jsonpath.spi.compiler.EvaluationContext; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.Set; |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
class EvaluationContextImpl implements EvaluationContext { |
||||
|
||||
private final Configuration configuration; |
||||
private final Object objectResult; |
||||
private final List<String> pathResult; |
||||
private final boolean isDefinite; |
||||
private int resultIndex = 0; |
||||
|
||||
EvaluationContextImpl(Configuration configuration, boolean isDefinite) { |
||||
this.configuration = configuration; |
||||
this.objectResult = configuration.getProvider().createArray(); |
||||
this.pathResult = new ArrayList<String>(); |
||||
this.isDefinite = isDefinite; |
||||
} |
||||
|
||||
void addResult(String path, Object model) { |
||||
pathResult.add(path); |
||||
configuration.getProvider().setProperty(objectResult, resultIndex, model); |
||||
resultIndex++; |
||||
} |
||||
|
||||
public JsonProvider jsonProvider() { |
||||
return configuration.getProvider(); |
||||
} |
||||
|
||||
public Set<Option> options() { |
||||
return configuration.getOptions(); |
||||
} |
||||
|
||||
@Override |
||||
public Configuration configuration() { |
||||
return configuration; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public <T> T get() { |
||||
if (isDefinite) { |
||||
return (T) jsonProvider().getProperty(objectResult, 0); |
||||
} |
||||
return (T) objectResult; |
||||
} |
||||
|
||||
@Override |
||||
public List<String> getPathList() { |
||||
return pathResult; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,74 @@
|
||||
package com.jayway.jsonpath.internal.spi.compiler; |
||||
|
||||
import com.jayway.jsonpath.Configuration; |
||||
import com.jayway.jsonpath.Filter; |
||||
import com.jayway.jsonpath.Filter2; |
||||
import com.jayway.jsonpath.InvalidPathException; |
||||
|
||||
import java.util.Collection; |
||||
|
||||
import static java.lang.String.format; |
||||
import static java.util.Arrays.asList; |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
class FilterPathToken extends PathToken { |
||||
|
||||
private static final String[] FRAGMENTS = { |
||||
"[?]", |
||||
"[?,?]", |
||||
"[?,?,?]", |
||||
"[?,?,?,?]", |
||||
"[?,?,?,?,?]" |
||||
}; |
||||
|
||||
private final Collection<Filter2> filters; |
||||
|
||||
public FilterPathToken(Filter2 filter) { |
||||
this.filters = asList(filter); |
||||
} |
||||
|
||||
public FilterPathToken(Collection<Filter2> filters) { |
||||
this.filters = filters; |
||||
} |
||||
|
||||
@Override |
||||
void evaluate(String currentPath, Object model, EvaluationContextImpl ctx) { |
||||
if (!ctx.jsonProvider().isArray(model)) { |
||||
throw new InvalidPathException(format("Filter: %s can only be applied to arrays. Current context is: %s", toString(), model)); |
||||
} |
||||
int idx = 0; |
||||
Iterable<Object> objects = ctx.jsonProvider().toIterable(model); |
||||
|
||||
for (Object idxModel : objects) { |
||||
if (accept(idxModel, ctx.configuration())) { |
||||
handleArrayIndex(idx, currentPath, model, ctx); |
||||
} |
||||
idx++; |
||||
} |
||||
} |
||||
|
||||
public boolean accept(Object obj, Configuration configuration) { |
||||
boolean accept = true; |
||||
|
||||
for (Filter2 filter : filters) { |
||||
if (!filter.apply (obj, configuration)) { |
||||
accept = false; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
return accept; |
||||
} |
||||
|
||||
@Override |
||||
public String getPathFragment() { |
||||
return FRAGMENTS[filters.size() - 1]; |
||||
} |
||||
|
||||
@Override |
||||
boolean isTokenDefinite() { |
||||
return false; |
||||
} |
||||
} |
@ -0,0 +1,530 @@
|
||||
package com.jayway.jsonpath.internal.spi.compiler; |
||||
|
||||
import com.jayway.jsonpath.Criteria; |
||||
import com.jayway.jsonpath.Criteria2; |
||||
import com.jayway.jsonpath.Filter; |
||||
import com.jayway.jsonpath.Filter2; |
||||
import com.jayway.jsonpath.InvalidPathException; |
||||
import com.jayway.jsonpath.internal.Cache; |
||||
import com.jayway.jsonpath.internal.Utils; |
||||
import com.jayway.jsonpath.spi.compiler.Path; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Iterator; |
||||
import java.util.LinkedList; |
||||
import java.util.List; |
||||
import java.util.regex.Pattern; |
||||
|
||||
import static com.jayway.jsonpath.internal.Utils.notEmpty; |
||||
import static java.util.Arrays.asList; |
||||
|
||||
public class PathCompiler { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PathCompiler.class); |
||||
|
||||
private static final String PROPERTY_OPEN = "['"; |
||||
private static final String PROPERTY_CLOSE = "']"; |
||||
private static final char DOCUMENT = '$'; |
||||
private static final char ANY = '*'; |
||||
private static final char PERIOD = '.'; |
||||
private static final char BRACKET_OPEN = '['; |
||||
private static final char BRACKET_CLOSE = ']'; |
||||
private static final Cache cache = new Cache(200); |
||||
|
||||
|
||||
|
||||
public static Path tokenize(String path, Filter2... filters) { |
||||
notEmpty(path, "Path may not be null empty"); |
||||
path = path.trim(); |
||||
|
||||
LinkedList<Filter2> filterList = new LinkedList<Filter2>(asList(filters)); |
||||
|
||||
if (!path.startsWith("$")) { |
||||
path = "$." + path; |
||||
} |
||||
|
||||
Path p = cache.get(path + filterList.toString()); |
||||
if(p != null){ |
||||
return p; |
||||
} |
||||
|
||||
RootPathToken root = null; |
||||
|
||||
|
||||
char[] chars = path.toCharArray(); |
||||
int i = 0; |
||||
int positions; |
||||
String fragment = ""; |
||||
|
||||
do { |
||||
char current = chars[i]; |
||||
|
||||
switch (current) { |
||||
case DOCUMENT: |
||||
fragment = "$"; |
||||
i++; |
||||
break; |
||||
case BRACKET_OPEN: |
||||
positions = fastForwardUntilClosed(chars, i); |
||||
fragment = new String(chars, i, positions); |
||||
i += positions; |
||||
break; |
||||
case PERIOD: |
||||
i++; |
||||
if (chars[i] == PERIOD) { |
||||
//This is a deep scan
|
||||
fragment = ".."; |
||||
i++; |
||||
} else { |
||||
positions = fastForward(chars, i); |
||||
if (positions == 0) { |
||||
continue; |
||||
|
||||
} else if (positions == 1 && chars[i] == '*') { |
||||
fragment = new String("[*]"); |
||||
} else { |
||||
assertValidFieldChars(chars, i, positions); |
||||
|
||||
fragment = PROPERTY_OPEN + new String(chars, i, positions) + PROPERTY_CLOSE; |
||||
} |
||||
i += positions; |
||||
} |
||||
break; |
||||
case ANY: |
||||
fragment = new String("[*]"); |
||||
i++; |
||||
break; |
||||
default: |
||||
positions = fastForward(chars, i); |
||||
|
||||
fragment = PROPERTY_OPEN + new String(chars, i, positions) + PROPERTY_CLOSE; |
||||
i += positions; |
||||
break; |
||||
} |
||||
if (root == null) { |
||||
root = (RootPathToken) PathComponentAnalyzer.analyze(fragment, filterList); |
||||
} else { |
||||
root.append(PathComponentAnalyzer.analyze(fragment, filterList)); |
||||
} |
||||
|
||||
} while (i < chars.length); |
||||
|
||||
Path pa = new CompiledPath(root); |
||||
|
||||
cache.put(path, pa); |
||||
|
||||
return pa; |
||||
} |
||||
|
||||
private static void assertValidFieldChars(char[] chars, int start, int positions) { |
||||
int i = start; |
||||
while (i < start + positions) { |
||||
char c = chars[i]; |
||||
|
||||
if (!Character.isLetterOrDigit(c) && c != '-' && c != '_' && c != '$' && c != '@') { |
||||
throw new InvalidPathException("Invalid field name! Use bracket notation if your filed names does not match pattern: ([a-zA-Z@][a-zA-Z0-9@\\$_\\-]*)$"); |
||||
} |
||||
i++; |
||||
} |
||||
} |
||||
|
||||
private static int fastForward(char[] chars, int index) { |
||||
int skipCount = 0; |
||||
while (index < chars.length) { |
||||
char current = chars[index]; |
||||
if (current == PERIOD || current == BRACKET_OPEN) { |
||||
break; |
||||
} |
||||
index++; |
||||
skipCount++; |
||||
} |
||||
return skipCount; |
||||
} |
||||
|
||||
private static int fastForwardUntilClosed(char[] chars, int index) { |
||||
int skipCount = 0; |
||||
int nestedBrackets = 0; |
||||
|
||||
//First char is always '[' no need to check it
|
||||
index++; |
||||
skipCount++; |
||||
|
||||
while (index < chars.length) { |
||||
char current = chars[index]; |
||||
|
||||
index++; |
||||
skipCount++; |
||||
|
||||
if (current == BRACKET_CLOSE && nestedBrackets == 0) { |
||||
break; |
||||
} |
||||
if (current == BRACKET_OPEN) { |
||||
nestedBrackets++; |
||||
} |
||||
if (current == BRACKET_CLOSE) { |
||||
nestedBrackets--; |
||||
} |
||||
} |
||||
return skipCount; |
||||
} |
||||
|
||||
|
||||
//---------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------
|
||||
static class PathComponentAnalyzer { |
||||
|
||||
private static final Pattern FILTER_PATTERN = Pattern.compile("^\\[\\s*\\?\\s*[,\\s*\\?]*?\\s*]$"); //[?] or [?, ?, ...]
|
||||
private char[] chars; |
||||
private int i; |
||||
private char current; |
||||
|
||||
private final LinkedList<Filter2> filterList; |
||||
private final String pathFragment; |
||||
|
||||
PathComponentAnalyzer(String pathFragment, LinkedList<Filter2> filterList) { |
||||
this.pathFragment = pathFragment; |
||||
this.filterList = filterList; |
||||
} |
||||
|
||||
static PathToken analyze(String pathFragment, LinkedList<Filter2> filterList) { |
||||
return new PathComponentAnalyzer(pathFragment, filterList).analyze(); |
||||
} |
||||
|
||||
public PathToken analyze() { |
||||
|
||||
if ("$".equals(pathFragment)) return new RootPathToken(); |
||||
else if ("..".equals(pathFragment)) return new ScanPathToken(); |
||||
else if ("[*]".equals(pathFragment)) return new WildcardPathToken(); |
||||
else if (".*".equals(pathFragment)) return new WildcardPathToken(); |
||||
else if ("[?]".equals(pathFragment)) return new FilterPathToken(filterList.poll()); |
||||
|
||||
else if (FILTER_PATTERN.matcher(pathFragment).matches()) { |
||||
final int criteriaCount = Utils.countMatches(pathFragment, "?"); |
||||
List<Filter2> filters = new ArrayList<Filter2>(criteriaCount); |
||||
for (int i = 0; i < criteriaCount; i++) { |
||||
filters.add(filterList.poll()); |
||||
} |
||||
return new FilterPathToken(filters); |
||||
} |
||||
|
||||
this.chars = pathFragment.toCharArray(); |
||||
this.i = 0; |
||||
do { |
||||
current = chars[i]; |
||||
|
||||
switch (current) { |
||||
case '?': |
||||
return analyzeCriteriaSequence(); |
||||
case '\'': |
||||
return analyzeProperty(); |
||||
default: |
||||
if (Character.isDigit(current) || current == ':' || current == '-' || current == '@') { |
||||
return analyzeArraySequence(); |
||||
} |
||||
i++; |
||||
break; |
||||
} |
||||
|
||||
|
||||
} while (i < chars.length); |
||||
|
||||
throw new InvalidPathException("Could not analyze path component: " + pathFragment); |
||||
} |
||||
|
||||
//[?(@.foo)]
|
||||
//[?(@['foo'])]
|
||||
//[?(@.foo == 'bar')]
|
||||
//[?(@['foo']['bar'] == 'bar')]
|
||||
//[?(@ == 'bar')]
|
||||
private PathToken analyzeCriteriaSequence() { |
||||
StringBuilder pathBuffer = new StringBuilder(); |
||||
StringBuilder operatorBuffer = new StringBuilder(); |
||||
StringBuilder valueBuffer = new StringBuilder(); |
||||
List<Criteria2> criteria = new ArrayList<Criteria2>(); |
||||
|
||||
int bracketCount = 0; |
||||
|
||||
boolean functionBracketOpened = false; |
||||
boolean functionBracketClosed = false; |
||||
|
||||
current = chars[++i]; //skip the '?'
|
||||
|
||||
while (current != ']' || bracketCount != 0) { |
||||
|
||||
switch (current) { |
||||
case '(': |
||||
functionBracketOpened = true; |
||||
break; |
||||
|
||||
case ')': |
||||
functionBracketClosed = true; |
||||
break; |
||||
|
||||
case '[': |
||||
bracketCount++; |
||||
pathBuffer.append(current); |
||||
break; |
||||
|
||||
case ']': |
||||
bracketCount--; |
||||
pathBuffer.append(current); |
||||
break; |
||||
|
||||
case '@': |
||||
pathBuffer.append('$'); |
||||
break; |
||||
|
||||
default: |
||||
if (bracketCount == 0 && isOperatorChar(current)) { |
||||
operatorBuffer.append(current); |
||||
|
||||
} else if (bracketCount == 0 && isLogicOperatorChar(current)) { |
||||
|
||||
if (isLogicOperatorChar(chars[i + 1])) { |
||||
char op1 = current; |
||||
char op2 = chars[++i]; |
||||
} |
||||
criteria.add(createCriteria(pathBuffer, operatorBuffer, valueBuffer)); |
||||
|
||||
pathBuffer = new StringBuilder(); |
||||
operatorBuffer = new StringBuilder(); |
||||
valueBuffer = new StringBuilder(); |
||||
|
||||
} else if (operatorBuffer.length() > 0) { |
||||
valueBuffer.append(current); |
||||
} else { |
||||
pathBuffer.append(current); |
||||
} |
||||
|
||||
break; |
||||
} |
||||
current = chars[++i]; |
||||
} |
||||
|
||||
if (!functionBracketOpened || !functionBracketClosed) { |
||||
throw new InvalidPathException("Function wrapping brackets are not matching. A filter function must match [?(<statement>)]"); |
||||
} |
||||
|
||||
criteria.add(createCriteria(pathBuffer, operatorBuffer, valueBuffer)); |
||||
|
||||
/* |
||||
Iterator<Criteria2> iterator = criteria.iterator(); |
||||
Criteria2 current = iterator.next(); |
||||
Filter2 filter = Filter2.filter(current); |
||||
while (iterator.hasNext()) { |
||||
filter. addCriteria(iterator.next()); |
||||
} |
||||
|
||||
return new FilterPathToken(filter); |
||||
*/ |
||||
Filter2 filter2 = Filter2.filter(criteria); |
||||
|
||||
return new FilterPathToken(filter2); |
||||
} |
||||
|
||||
private Criteria2 createCriteria(StringBuilder pathBuffer, StringBuilder operatorBuffer, StringBuilder valueBuffer) { |
||||
return Criteria2.create(pathBuffer.toString().trim(), operatorBuffer.toString().trim(), valueBuffer.toString().trim()); |
||||
|
||||
/* |
||||
String value = valueBuffer.toString().trim(); |
||||
Path path = PathCompiler.tokenize(pathBuffer.toString().trim()); |
||||
String operator = operatorBuffer.toString().trim(); |
||||
|
||||
|
||||
|
||||
|
||||
if (operator.isEmpty() && value.isEmpty()) { |
||||
return Criteria.where(path).exists(true); |
||||
} else { |
||||
return Criteria.where(path).matches(operator, value); |
||||
} |
||||
*/ |
||||
} |
||||
|
||||
private boolean isAnd(char c) { |
||||
return c == '&'; |
||||
} |
||||
|
||||
private boolean isOr(char c) { |
||||
if (c == '|') { |
||||
throw new UnsupportedOperationException("OR operator is not supported."); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
private boolean isLogicOperatorChar(char c) { |
||||
return isAnd(c) || isOr(c); |
||||
} |
||||
|
||||
private boolean isOperatorChar(char c) { |
||||
return c == '=' || c == '!' || c == '<' || c == '>'; |
||||
} |
||||
|
||||
//"['foo']"
|
||||
private PathToken analyzeProperty() { |
||||
String property = null; |
||||
|
||||
StringBuilder buffer = new StringBuilder(); |
||||
|
||||
boolean propertyIsOpen = false; |
||||
|
||||
while (current != ']') { |
||||
switch (current) { |
||||
case '\'': |
||||
if (propertyIsOpen) { |
||||
property = buffer.toString(); |
||||
propertyIsOpen = false; |
||||
} else { |
||||
propertyIsOpen = true; |
||||
} |
||||
break; |
||||
default: |
||||
if (propertyIsOpen) { |
||||
buffer.append(current); |
||||
} |
||||
break; |
||||
} |
||||
current = chars[++i]; |
||||
} |
||||
return new PropertyPathToken(property); |
||||
} |
||||
|
||||
|
||||
//"[-1:]" sliceFrom
|
||||
//"[:1]" sliceTo
|
||||
//"[0:5]" sliceBetween
|
||||
//"[1]"
|
||||
//"[1,2,3]"
|
||||
//"[(@.length - 1)]"
|
||||
private PathToken analyzeArraySequence() { |
||||
StringBuilder buffer = new StringBuilder(); |
||||
List<Integer> numbers = new ArrayList<Integer>(); |
||||
|
||||
boolean contextSize = (current == '@'); |
||||
boolean sliceTo = false; |
||||
boolean sliceFrom = false; |
||||
boolean sliceBetween = false; |
||||
boolean indexSequence = false; |
||||
boolean singleIndex = false; |
||||
|
||||
if (contextSize) { |
||||
|
||||
current = chars[++i]; |
||||
current = chars[++i]; |
||||
while (current != '-') { |
||||
if (current == ' ' || current == '(' || current == ')') { |
||||
current = chars[++i]; |
||||
continue; |
||||
} |
||||
buffer.append(current); |
||||
current = chars[++i]; |
||||
} |
||||
String function = buffer.toString(); |
||||
buffer = new StringBuilder(); |
||||
if (!function.equals("size") && !function.equals("length")) { |
||||
throw new InvalidPathException("Invalid function: @." + function + ". Supported functions are: [(@.length - n)] and [(@.size() - n)]"); |
||||
} |
||||
while (current != ')') { |
||||
if (current == ' ') { |
||||
current = chars[++i]; |
||||
continue; |
||||
} |
||||
buffer.append(current); |
||||
current = chars[++i]; |
||||
} |
||||
|
||||
} else { |
||||
|
||||
|
||||
while (Character.isDigit(current) || current == ',' || current == ' ' || current == ':' || current == '-') { |
||||
|
||||
switch (current) { |
||||
case ' ': |
||||
break; |
||||
case ':': |
||||
if (buffer.length() == 0) { |
||||
//this is a tail slice [:12]
|
||||
sliceTo = true; |
||||
current = chars[++i]; |
||||
while (Character.isDigit(current) || current == ' ' || current == '-') { |
||||
if (current != ' ') { |
||||
buffer.append(current); |
||||
} |
||||
current = chars[++i]; |
||||
} |
||||
numbers.add(Integer.parseInt(buffer.toString())); |
||||
buffer = new StringBuilder(); |
||||
} else { |
||||
//we now this starts with [12:???
|
||||
numbers.add(Integer.parseInt(buffer.toString())); |
||||
buffer = new StringBuilder(); |
||||
current = chars[++i]; |
||||
|
||||
//this is a tail slice [:12]
|
||||
while (Character.isDigit(current) || current == ' ' || current == '-') { |
||||
if (current != ' ') { |
||||
buffer.append(current); |
||||
} |
||||
current = chars[++i]; |
||||
} |
||||
|
||||
if (buffer.length() == 0) { |
||||
sliceFrom = true; |
||||
} else { |
||||
sliceBetween = true; |
||||
numbers.add(Integer.parseInt(buffer.toString())); |
||||
buffer = new StringBuilder(); |
||||
} |
||||
} |
||||
break; |
||||
case ',': |
||||
numbers.add(Integer.parseInt(buffer.toString())); |
||||
buffer = new StringBuilder(); |
||||
indexSequence = true; |
||||
break; |
||||
default: |
||||
buffer.append(current); |
||||
break; |
||||
} |
||||
if (current == ']') { |
||||
break; |
||||
} |
||||
current = chars[++i]; |
||||
} |
||||
} |
||||
if (buffer.length() > 0) { |
||||
numbers.add(Integer.parseInt(buffer.toString())); |
||||
} |
||||
singleIndex = (numbers.size() == 1) && !sliceTo && !sliceFrom && !contextSize; |
||||
|
||||
if (logger.isTraceEnabled()) { |
||||
logger.debug("numbers are : {}", numbers.toString()); |
||||
logger.debug("sequence is singleNumber : {}", singleIndex); |
||||
logger.debug("sequence is numberSequence : {}", indexSequence); |
||||
logger.debug("sequence is sliceFrom : {}", sliceFrom); |
||||
logger.debug("sequence is sliceTo : {}", sliceTo); |
||||
logger.debug("sequence is sliceBetween : {}", sliceBetween); |
||||
logger.debug("sequence is contextFetch : {}", contextSize); |
||||
logger.debug("---------------------------------------------"); |
||||
} |
||||
ArrayPathToken.Operation operation = null; |
||||
|
||||
if (singleIndex) operation = ArrayPathToken.Operation.SINGLE_INDEX; |
||||
else if (indexSequence) operation = ArrayPathToken.Operation.INDEX_SEQUENCE; |
||||
else if (sliceFrom) operation = ArrayPathToken.Operation.SLICE_FROM; |
||||
else if (sliceTo) operation = ArrayPathToken.Operation.SLICE_TO; |
||||
else if (sliceBetween) operation = ArrayPathToken.Operation.SLICE_BETWEEN; |
||||
else if (contextSize) operation = ArrayPathToken.Operation.CONTEXT_SIZE; |
||||
|
||||
assert operation != null; |
||||
|
||||
return new ArrayPathToken(numbers, operation); |
||||
|
||||
} |
||||
} |
||||
} |
@ -0,0 +1,108 @@
|
||||
package com.jayway.jsonpath.internal.spi.compiler; |
||||
|
||||
import com.jayway.jsonpath.Option; |
||||
import com.jayway.jsonpath.PathNotFoundException; |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
abstract class PathToken { |
||||
|
||||
private PathToken next; |
||||
private Boolean definite = null; |
||||
|
||||
PathToken appendTailToken(PathToken next) { |
||||
this.next = next; |
||||
return next; |
||||
} |
||||
|
||||
void handleObjectProperty(String currentPath, Object model, EvaluationContextImpl ctx, String property) { |
||||
String evalPath = currentPath + "['" + property + "']"; |
||||
Object propertyVal = readObjectProperty(property, model, ctx); |
||||
if (isLeaf()) { |
||||
ctx.addResult(evalPath, propertyVal); |
||||
} else { |
||||
next().evaluate(evalPath, propertyVal, ctx); |
||||
} |
||||
} |
||||
|
||||
|
||||
private Object readObjectProperty(String property, Object model, EvaluationContextImpl ctx) { |
||||
if (ctx.options().contains(Option.THROW_ON_MISSING_PROPERTY) && !ctx.jsonProvider().getPropertyKeys(model).contains(property)) { |
||||
throw new PathNotFoundException("Path [" + property + "] not found in the current context:\n" + ctx.jsonProvider().toJson(model)); |
||||
} |
||||
return ctx.jsonProvider().getProperty(model, property); |
||||
} |
||||
|
||||
void handleArrayIndex(int index, String currentPath, Object json, EvaluationContextImpl ctx) { |
||||
String evalPath = currentPath + "[" + index + "]"; |
||||
Object evalHit = ctx.jsonProvider().getProperty(json, index); |
||||
if (isLeaf()) { |
||||
ctx.addResult(evalPath, evalHit); |
||||
} else { |
||||
next().evaluate(evalPath, evalHit, ctx); |
||||
} |
||||
} |
||||
|
||||
|
||||
PathToken next() { |
||||
if (isLeaf()) { |
||||
throw new IllegalStateException("Current path token is a leaf"); |
||||
} |
||||
return next; |
||||
} |
||||
|
||||
boolean isLeaf() { |
||||
return next == null; |
||||
} |
||||
|
||||
|
||||
public int getTokenCount() { |
||||
int cnt = 1; |
||||
PathToken token = this; |
||||
|
||||
while (!token.isLeaf()){ |
||||
token = token.next(); |
||||
cnt++; |
||||
} |
||||
return cnt; |
||||
} |
||||
|
||||
public boolean isPathDefinite() { |
||||
if(definite != null){ |
||||
return definite.booleanValue(); |
||||
} |
||||
boolean isDefinite = isTokenDefinite(); |
||||
if (isDefinite && !isLeaf()) { |
||||
isDefinite = next.isPathDefinite(); |
||||
} |
||||
definite = isDefinite; |
||||
return isDefinite; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
if (isLeaf()) { |
||||
return getPathFragment(); |
||||
} else { |
||||
return getPathFragment() + next().toString(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return toString().hashCode(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object obj) { |
||||
return super.equals(obj); |
||||
} |
||||
|
||||
abstract void evaluate(String currentPath, Object model, EvaluationContextImpl ctx); |
||||
|
||||
abstract boolean isTokenDefinite(); |
||||
|
||||
abstract String getPathFragment(); |
||||
|
||||
} |
@ -0,0 +1,41 @@
|
||||
package com.jayway.jsonpath.internal.spi.compiler; |
||||
|
||||
import com.jayway.jsonpath.PathNotFoundException; |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
class PropertyPathToken extends PathToken { |
||||
|
||||
private final String property; |
||||
|
||||
public PropertyPathToken(String property) { |
||||
this.property = property; |
||||
} |
||||
|
||||
public String getProperty() { |
||||
return property; |
||||
} |
||||
|
||||
@Override |
||||
void evaluate(String currentPath, Object model, EvaluationContextImpl ctx) { |
||||
if (!ctx.jsonProvider().isMap(model)) { |
||||
throw new PathNotFoundException("Property " + currentPath + " not found!"); |
||||
} |
||||
|
||||
handleObjectProperty(currentPath, model, ctx, property); |
||||
} |
||||
|
||||
@Override |
||||
boolean isTokenDefinite() { |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public String getPathFragment() { |
||||
return new StringBuilder() |
||||
.append("['") |
||||
.append(property) |
||||
.append("']").toString(); |
||||
} |
||||
} |
@ -0,0 +1,66 @@
|
||||
package com.jayway.jsonpath.internal.spi.compiler; |
||||
|
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
class RootPathToken extends PathToken /*implements Path*/ { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(RootPathToken.class); |
||||
|
||||
private PathToken tail; |
||||
private int tokenCount; |
||||
|
||||
public RootPathToken() { |
||||
this.tail = this; |
||||
this.tokenCount = 1; |
||||
} |
||||
|
||||
@Override |
||||
public int getTokenCount() { |
||||
return tokenCount; |
||||
} |
||||
|
||||
public RootPathToken append(PathToken next) { |
||||
this.tail = tail.appendTailToken(next); |
||||
this.tokenCount++; |
||||
return this; |
||||
} |
||||
/* |
||||
@Override |
||||
public EvaluationContextImpl evaluate(Object model, Configuration configuration) { |
||||
if(logger.isDebugEnabled()) { |
||||
logger.debug("Evaluating path: {}", toString()); |
||||
} |
||||
|
||||
EvaluationContextImpl ctx = new EvaluationContextImpl(configuration, isDefinite()); |
||||
evaluate("", model, ctx); |
||||
|
||||
if(logger.isDebugEnabled()) { |
||||
logger.debug("Found:\n{}", JsonFormatter.prettyPrint(ctx.configuration().getProvider().toJson(ctx.getPathList()))); |
||||
} |
||||
|
||||
return ctx; |
||||
} |
||||
*/ |
||||
@Override |
||||
void evaluate(String currentPath, Object model, EvaluationContextImpl ctx) { |
||||
if (isLeaf()) { |
||||
ctx.addResult("$", model); |
||||
} else { |
||||
next().evaluate("$", model, ctx); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public String getPathFragment() { |
||||
return "$"; |
||||
} |
||||
|
||||
@Override |
||||
boolean isTokenDefinite() { |
||||
return true; |
||||
} |
||||
} |
@ -0,0 +1,175 @@
|
||||
package com.jayway.jsonpath.internal.spi.compiler; |
||||
|
||||
import java.util.Collection; |
||||
import java.util.LinkedHashMap; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
class ScanPathToken extends PathToken { |
||||
|
||||
@Override |
||||
void evaluate(String currentPath, Object model, EvaluationContextImpl ctx) { |
||||
|
||||
if (isLeaf()) { |
||||
ctx.addResult(currentPath, model); |
||||
} |
||||
|
||||
Predicate predicate = createScanPredicate(next(), ctx); |
||||
Map<String, Object> predicateMatches = new LinkedHashMap<String, Object>(); |
||||
|
||||
walk(currentPath, model, ctx, predicate, predicateMatches); |
||||
|
||||
//Filters has already been evaluated
|
||||
PathToken next = next(); |
||||
if (next instanceof FilterPathToken) { |
||||
if (next.isLeaf()) { |
||||
for (Map.Entry<String, Object> match : predicateMatches.entrySet()) { |
||||
ctx.addResult(match.getKey(), match.getValue()); |
||||
} |
||||
return; |
||||
} else { |
||||
next = next.next(); |
||||
} |
||||
} |
||||
|
||||
for (Map.Entry<String, Object> match : predicateMatches.entrySet()) { |
||||
next.evaluate(match.getKey(), match.getValue(), ctx); |
||||
} |
||||
} |
||||
|
||||
public void walk(String currentPath, Object model, EvaluationContextImpl ctx, Predicate predicate, Map<String, Object> predicateMatches) { |
||||
if (ctx.jsonProvider().isMap(model)) { |
||||
walkObject(currentPath, model, ctx, predicate, predicateMatches); |
||||
} else if (ctx.jsonProvider().isArray(model)) { |
||||
walkArray(currentPath, model, ctx, predicate, predicateMatches); |
||||
} |
||||
} |
||||
|
||||
public void walkArray(String currentPath, Object model, EvaluationContextImpl ctx, Predicate predicate, Map<String, Object> predicateMatches) { |
||||
|
||||
if (predicate.matches(model)) { |
||||
predicateMatches.put(currentPath, model); |
||||
} |
||||
|
||||
Iterable<Object> models = ctx.jsonProvider().toIterable(model); |
||||
int idx = 0; |
||||
for (Object evalModel : models) { |
||||
String evalPath = currentPath + "[" + idx + "]"; |
||||
|
||||
if (predicate.clazz().equals(FilterPathToken.class)) { |
||||
if (predicate.matches(evalModel)) { |
||||
predicateMatches.put(evalPath, evalModel); |
||||
} |
||||
} |
||||
|
||||
walk(evalPath, evalModel, ctx, predicate, predicateMatches); |
||||
idx++; |
||||
} |
||||
} |
||||
|
||||
public void walkObject(String currentPath, Object model, EvaluationContextImpl ctx, Predicate predicate, Map<String, Object> predicateMatches) { |
||||
|
||||
if (predicate.matches(model)) { |
||||
predicateMatches.put(currentPath, model); |
||||
} |
||||
Collection<String> properties = ctx.jsonProvider().getPropertyKeys(model); |
||||
|
||||
for (String property : properties) { |
||||
String evalPath = currentPath + "['" + property + "']"; |
||||
Object propertyModel = ctx.jsonProvider().getProperty(model, property); |
||||
walk(evalPath, propertyModel, ctx, predicate, predicateMatches); |
||||
} |
||||
|
||||
} |
||||
|
||||
private Predicate createScanPredicate(final PathToken target, final EvaluationContextImpl ctx) { |
||||
if (target instanceof PropertyPathToken) { |
||||
|
||||
return new Predicate() { |
||||
private PropertyPathToken propertyPathToken = (PropertyPathToken) target; |
||||
|
||||
@Override |
||||
public Class<?> clazz() { |
||||
return PropertyPathToken.class; |
||||
} |
||||
|
||||
@Override |
||||
public boolean matches(Object model) { |
||||
Collection<String> keys = ctx.jsonProvider().getPropertyKeys(model); |
||||
return keys.contains(propertyPathToken.getProperty()); |
||||
} |
||||
}; |
||||
} else if (target instanceof ArrayPathToken) { |
||||
return new Predicate() { |
||||
|
||||
@Override |
||||
public Class<?> clazz() { |
||||
return ArrayPathToken.class; |
||||
} |
||||
|
||||
@Override |
||||
public boolean matches(Object model) { |
||||
return ctx.jsonProvider().isArray(model); |
||||
} |
||||
}; |
||||
} else if (target instanceof WildcardPathToken) { |
||||
return new Predicate() { |
||||
|
||||
@Override |
||||
public Class<?> clazz() { |
||||
return WildcardPathToken.class; |
||||
} |
||||
|
||||
@Override |
||||
public boolean matches(Object model) { |
||||
return true; |
||||
} |
||||
}; |
||||
} else if (target instanceof FilterPathToken) { |
||||
return new Predicate() { |
||||
private FilterPathToken filterPathToken = (FilterPathToken) target; |
||||
|
||||
@Override |
||||
public Class<?> clazz() { |
||||
return FilterPathToken.class; |
||||
} |
||||
|
||||
@Override |
||||
public boolean matches(Object model) { |
||||
return filterPathToken.accept(model, ctx.configuration()); |
||||
} |
||||
}; |
||||
} else { |
||||
return new Predicate() { |
||||
@Override |
||||
public Class<?> clazz() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public boolean matches(Object model) { |
||||
return false; |
||||
} |
||||
}; |
||||
} |
||||
} |
||||
|
||||
|
||||
@Override |
||||
boolean isTokenDefinite() { |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
public String getPathFragment() { |
||||
return ".."; |
||||
} |
||||
|
||||
private interface Predicate { |
||||
Class<?> clazz(); |
||||
|
||||
boolean matches(Object model); |
||||
} |
||||
} |
@ -0,0 +1,31 @@
|
||||
package com.jayway.jsonpath.internal.spi.compiler; |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
class WildcardPathToken extends PathToken { |
||||
|
||||
@Override |
||||
void evaluate(String currentPath, Object model, EvaluationContextImpl ctx) { |
||||
if (ctx.jsonProvider().isMap(model)) { |
||||
for (String property : ctx.jsonProvider().getPropertyKeys(model)) { |
||||
handleObjectProperty(currentPath, model, ctx, property); |
||||
} |
||||
} else if (ctx.jsonProvider().isArray(model)) { |
||||
for (int idx = 0; idx < ctx.jsonProvider().length(model); idx++) { |
||||
handleArrayIndex(idx, currentPath, model, ctx); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
@Override |
||||
boolean isTokenDefinite() { |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
public String getPathFragment() { |
||||
return "[*]"; |
||||
} |
||||
} |
@ -1,29 +0,0 @@
|
||||
/* |
||||
* Copyright 2011 the original author or authors. |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.jayway.jsonpath.spi; |
||||
|
||||
import java.util.Collection; |
||||
|
||||
/** |
||||
* @author Kalle Stenflo |
||||
*/ |
||||
public interface MappingProvider { |
||||
|
||||
|
||||
public <T> T convertValue(Object fromValue, Class<T> toValueType) throws IllegalArgumentException; |
||||
|
||||
public <T extends Collection<E>, E> T convertValue(Object fromValue, Class<T> collectionType, Class<E> elementType) throws IllegalArgumentException; |
||||
|
||||
} |
@ -1,54 +0,0 @@
|
||||
/* |
||||
* Copyright 2011 the original author or authors. |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.jayway.jsonpath.spi; |
||||
|
||||
import com.jayway.jsonpath.spi.impl.JacksonProvider; |
||||
|
||||
/** |
||||
* @author Kalle Stenflo |
||||
*/ |
||||
public abstract class MappingProviderFactory { |
||||
|
||||
|
||||
public static MappingProviderFactory factory = new MappingProviderFactory() { |
||||
|
||||
private MappingProvider provider = null; |
||||
|
||||
@Override |
||||
protected MappingProvider create() { |
||||
if (this.provider == null) { |
||||
synchronized (MappingProviderFactory.class) { |
||||
try { |
||||
Class.forName("org.codehaus.jackson.map.ObjectMapper"); |
||||
|
||||
provider = new JacksonProvider(); |
||||
return provider; |
||||
} catch (ClassNotFoundException e) { |
||||
throw new RuntimeException("org.codehaus.jackson.map.ObjectMapper not found on classpath. This is an optional dependency needed for POJO conversions.", e); |
||||
} |
||||
} |
||||
} else { |
||||
return provider; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
protected abstract MappingProvider create(); |
||||
|
||||
|
||||
public static MappingProvider createProvider() { |
||||
return factory.create(); |
||||
} |
||||
} |
@ -0,0 +1,16 @@
|
||||
package com.jayway.jsonpath.spi.compiler; |
||||
|
||||
import com.jayway.jsonpath.Configuration; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
public interface EvaluationContext { |
||||
Configuration configuration(); |
||||
|
||||
<T> T get(); |
||||
|
||||
List<String> getPathList(); |
||||
} |
@ -0,0 +1,15 @@
|
||||
package com.jayway.jsonpath.spi.compiler; |
||||
|
||||
import com.jayway.jsonpath.Configuration; |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
public interface Path { |
||||
EvaluationContext evaluate(Object model, Configuration configuration); |
||||
|
||||
boolean isDefinite(); |
||||
|
||||
int tokenCount(); |
||||
|
||||
} |
@ -0,0 +1,4 @@
|
||||
package com.jayway.jsonpath.spi.compiler; |
||||
|
||||
public interface PathCompiler { |
||||
} |
@ -1,4 +1,4 @@
|
||||
package com.jayway.jsonpath.spi; |
||||
package com.jayway.jsonpath.spi.http; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
@ -0,0 +1,77 @@
|
||||
package com.jayway.jsonpath; |
||||
|
||||
import com.jayway.jsonpath.spi.json.JsonProvider; |
||||
import com.jayway.jsonpath.spi.json.JsonProviderFactory; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.Set; |
||||
|
||||
public class DeepScanTest { |
||||
|
||||
private static final String DOCUMENT = "{\n" + |
||||
" \"store\":{\n" + |
||||
" \"book\":[\n" + |
||||
" {\n" + |
||||
" \"category\":\"reference\",\n" + |
||||
" \"author\":\"Nigel Rees\",\n" + |
||||
" \"title\":\"Sayings of the Century\",\n" + |
||||
" \"price\":8.95\n" + |
||||
" },\n" + |
||||
" {\n" + |
||||
" \"category\":\"fiction\",\n" + |
||||
" \"author\":\"Evelyn Waugh\",\n" + |
||||
" \"title\":\"Sword of Honour\",\n" + |
||||
" \"price\":12.99\n" + |
||||
" },\n" + |
||||
" {\n" + |
||||
" \"category\":\"fiction\",\n" + |
||||
" \"author\":\"J. R. R. Tolkien\",\n" + |
||||
" \"title\":\"The Lord of the Rings\",\n" + |
||||
" \"isbn\":\"0-395-19395-8\",\n" + |
||||
" \"price\":22.99\n" + |
||||
" }\n" + |
||||
" ],\n" + |
||||
" \"bicycle\":{\n" + |
||||
" \"color\":\"red\",\n" + |
||||
" \"price\":19.95\n" + |
||||
" }\n" + |
||||
" }\n" + |
||||
"}"; |
||||
|
||||
private static final JsonProvider prov = JsonProviderFactory.createProvider(); |
||||
private static final Set<Option> opts = Collections.emptySet(); |
||||
/* |
||||
@Test |
||||
public void correct_path() { |
||||
|
||||
System.out.println(DOCUMENT); |
||||
|
||||
System.out.println(PathEvaluator.evaluate("$.store..", DOCUMENT, prov, opts)); |
||||
} |
||||
|
||||
@Test |
||||
public void a_string_property_can_be_scanned_for() { |
||||
|
||||
PathEvaluationResult result = PathEvaluator.evaluate("$.store..category", DOCUMENT, prov, opts); |
||||
|
||||
assertThat(result.getPathList(), hasItems("$['store']['book'][0]['category']", "$['store']['book'][1]['category']", "$['store']['book'][2]['category']")); |
||||
|
||||
assertThat(result.getResultList(), hasItems( |
||||
Matchers.<Object>is("reference"), |
||||
Matchers.<Object>is("fiction"), |
||||
Matchers.<Object>is("fiction"))); |
||||
|
||||
System.out.println(result.toString()); |
||||
} |
||||
|
||||
@Test |
||||
public void a_path_can_end_with_deep_scan() { |
||||
|
||||
String json = "{\"items\":[1, 3, 5, 7, 8, 13, 20]}"; |
||||
|
||||
//System.out.println( JsonPath.read(json, "$.."));
|
||||
System.out.println(PathEvaluator.evaluate("$..", json, prov, opts).toString()); |
||||
|
||||
} |
||||
*/ |
||||
} |
@ -0,0 +1,388 @@
|
||||
package com.jayway.jsonpath; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.regex.Pattern; |
||||
|
||||
import static com.jayway.jsonpath.Criteria2.*; |
||||
import static com.jayway.jsonpath.Filter2.filter; |
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
public class Filter2Test { |
||||
|
||||
Configuration conf = Configuration.defaultConfiguration(); |
||||
Object json = conf.getProvider().parse( |
||||
"{" + |
||||
" \"int-key\" : 1, " + |
||||
" \"long-key\" : 3000000000, " + |
||||
" \"double-key\" : 10.1, " + |
||||
" \"boolean-key\" : true, " + |
||||
" \"null-key\" : null, " + |
||||
" \"string-key\" : \"string\", " + |
||||
" \"string-key-empty\" : \"\", " + |
||||
" \"char-key\" : \"c\", " + |
||||
" \"arr-empty\" : [], " + |
||||
" \"int-arr\" : [0,1,2,3,4], " + |
||||
" \"string-arr\" : [\"a\",\"b\",\"c\",\"d\",\"e\"] " + |
||||
"}" |
||||
); |
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// EQ
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
@Test |
||||
public void int_eq_evals() { |
||||
assertThat(filter(where("int-key").eq(1)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("int-key").eq(666)).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
|
||||
@Test |
||||
public void long_eq_evals() { |
||||
assertThat(filter(where("long-key").eq(3000000000L)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("long-key").eq(666L)).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
|
||||
@Test |
||||
public void double_eq_evals() { |
||||
assertThat(filter(where("double-key").eq(10.1D)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("double-key").eq(10.10D)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("double-key").eq(10.11D)).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
|
||||
@Test |
||||
public void string_eq_evals() { |
||||
assertThat(filter(where("string-key").eq("string")).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("string-key").eq("666")).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
|
||||
@Test |
||||
public void boolean_eq_evals() { |
||||
assertThat(filter(where("boolean-key").eq(true)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("boolean-key").eq(false)).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
|
||||
@Test |
||||
public void null_eq_evals() { |
||||
assertThat(filter(where("null-key").eq(null)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("null-key").eq("666")).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("string-key").eq(null)).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// NE
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
@Test |
||||
public void int_ne_evals() { |
||||
assertThat(filter(where("int-key").ne(1)).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("int-key").ne(666)).apply(json, conf)).isEqualTo(true); |
||||
} |
||||
|
||||
@Test |
||||
public void long_ne_evals() { |
||||
assertThat(filter(where("long-key").ne(3000000000L)).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("long-key").ne(666L)).apply(json, conf)).isEqualTo(true); |
||||
} |
||||
|
||||
@Test |
||||
public void double_ne_evals() { |
||||
assertThat(filter(where("double-key").ne(10.1D)).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("double-key").ne(10.10D)).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("double-key").ne(10.11D)).apply(json, conf)).isEqualTo(true); |
||||
} |
||||
|
||||
@Test |
||||
public void string_ne_evals() { |
||||
assertThat(filter(where("string-key").ne("string")).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("string-key").ne("666")).apply(json, conf)).isEqualTo(true); |
||||
} |
||||
|
||||
@Test |
||||
public void boolean_ne_evals() { |
||||
assertThat(filter(where("boolean-key").ne(true)).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("boolean-key").ne(false)).apply(json, conf)).isEqualTo(true); |
||||
} |
||||
|
||||
@Test |
||||
public void null_ne_evals() { |
||||
assertThat(filter(where("null-key").ne(null)).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("null-key").ne("666")).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("string-key").ne(null)).apply(json, conf)).isEqualTo(true); |
||||
} |
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// LT
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
@Test |
||||
public void int_lt_evals() { |
||||
assertThat(filter(where("int-key").lt(10)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("int-key").lt(0)).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
|
||||
@Test |
||||
public void long_lt_evals() { |
||||
assertThat(filter(where("long-key").lt(4000000000L)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("long-key").lt(666L)).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
|
||||
@Test |
||||
public void double_lt_evals() { |
||||
assertThat(filter(where("double-key").lt(100.1D)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("double-key").lt(1.1D)).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
|
||||
@Test |
||||
public void string_lt_evals() { |
||||
assertThat(filter(where("char-key").lt("x")).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("char-key").lt("a")).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// LTE
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
@Test |
||||
public void int_lte_evals() { |
||||
assertThat(filter(where("int-key").lte(10)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("int-key").lte(1)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("int-key").lte(0)).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
|
||||
@Test |
||||
public void long_lte_evals() { |
||||
assertThat(filter(where("long-key").lte(4000000000L)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("long-key").lte(3000000000L)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("long-key").lte(666L)).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
|
||||
@Test |
||||
public void double_lte_evals() { |
||||
assertThat(filter(where("double-key").lte(100.1D)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("double-key").lte(10.1D)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("double-key").lte(1.1D)).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// GT
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
@Test |
||||
public void int_gt_evals() { |
||||
assertThat(filter(where("int-key").gt(10)).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("int-key").gt(0)).apply(json, conf)).isEqualTo(true); |
||||
} |
||||
|
||||
@Test |
||||
public void long_gt_evals() { |
||||
assertThat(filter(where("long-key").gt(4000000000L)).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("long-key").gt(666L)).apply(json, conf)).isEqualTo(true); |
||||
} |
||||
|
||||
@Test |
||||
public void double_gt_evals() { |
||||
assertThat(filter(where("double-key").gt(100.1D)).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("double-key").gt(1.1D)).apply(json, conf)).isEqualTo(true); |
||||
} |
||||
|
||||
@Test |
||||
public void string_gt_evals() { |
||||
assertThat(filter(where("char-key").gt("x")).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("char-key").gt("a")).apply(json, conf)).isEqualTo(true); |
||||
} |
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// GTE
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
@Test |
||||
public void int_gte_evals() { |
||||
assertThat(filter(where("int-key").gte(10)).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("int-key").gte(1)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("int-key").gte(0)).apply(json, conf)).isEqualTo(true); |
||||
} |
||||
|
||||
@Test |
||||
public void long_gte_evals() { |
||||
assertThat(filter(where("long-key").gte(4000000000L)).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("long-key").gte(3000000000L)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("long-key").gte(666L)).apply(json, conf)).isEqualTo(true); |
||||
} |
||||
|
||||
@Test |
||||
public void double_gte_evals() { |
||||
assertThat(filter(where("double-key").gte(100.1D)).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("double-key").gte(10.1D)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("double-key").gte(1.1D)).apply(json, conf)).isEqualTo(true); |
||||
} |
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Regex
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
@Test |
||||
public void string_regex_evals() { |
||||
assertThat(filter(where("string-key").regex(Pattern.compile("^string$"))).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("string-key").regex(Pattern.compile("^tring$"))).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("null-key").regex(Pattern.compile("^string$"))).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("int-key").regex(Pattern.compile("^string$"))).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// IN
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
@Test |
||||
public void string_in_evals() { |
||||
assertThat(filter(where("string-key").in("a", null, "string")).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("string-key").in("a", null)).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("null-key").in("a", null)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("null-key").in("a", "b")).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("string-arr").in("a")).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// NIN
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
@Test |
||||
public void string_nin_evals() { |
||||
assertThat(filter(where("string-key").nin("a", null, "string")).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("string-key").nin("a", null)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("null-key").nin("a", null)).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("null-key").nin("a", "b")).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("string-arr").nin("a")).apply(json, conf)).isEqualTo(true); |
||||
|
||||
} |
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// ALL
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
@Test |
||||
public void int_all_evals() { |
||||
assertThat(filter(where("int-arr").all(0,1)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("int-arr").all(0,7)).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
@Test |
||||
public void string_all_evals() { |
||||
assertThat(filter(where("string-arr").all("a","b")).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("string-arr").all("a","x")).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
@Test |
||||
public void not_array_all_evals() { |
||||
assertThat(filter(where("string-key").all("a","b")).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// SIZE
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
@Test |
||||
public void array_size_evals() { |
||||
assertThat(filter(where("string-arr").size(5)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("string-arr").size(7)).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
|
||||
@Test |
||||
public void string_size_evals() { |
||||
assertThat(filter(where("string-key").size(6)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("string-key").size(7)).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
|
||||
@Test |
||||
public void other_size_evals() { |
||||
assertThat(filter(where("int-key").size(6)).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
|
||||
@Test |
||||
public void null_size_evals() { |
||||
assertThat(filter(where("null-key").size(6)).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// EXISTS
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
@Test |
||||
public void exists_evals() { |
||||
assertThat(filter(where("string-key").exists(true)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("string-key").exists(false)).apply(json, conf)).isEqualTo(false); |
||||
|
||||
assertThat(filter(where("missing-key").exists(true)).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("missing-key").exists(false)).apply(json, conf)).isEqualTo(true); |
||||
} |
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// TYPE
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
@Test |
||||
public void type_evals() { |
||||
assertThat(filter(where("string-key").type(String.class)).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("string-key").type(Integer.class)).apply(json, conf)).isEqualTo(false); |
||||
|
||||
assertThat(filter(where("int-key").type(String.class)).apply(json, conf)).isEqualTo(false); |
||||
assertThat(filter(where("int-key").type(Integer.class)).apply(json, conf)).isEqualTo(true); |
||||
|
||||
assertThat(filter(where("null-key").type(String.class)).apply(json, conf)).isEqualTo(false); |
||||
|
||||
assertThat(filter(where("int-arr").type(List.class)).apply(json, conf)).isEqualTo(true); |
||||
} |
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// NOT_EMPTY
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
@Test |
||||
public void not_empty_evals() { |
||||
assertThat(filter(where("string-key").notEmpty()).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("string-key-empty").notEmpty()).apply(json, conf)).isEqualTo(false); |
||||
|
||||
assertThat(filter(where("int-arr").notEmpty()).apply(json, conf)).isEqualTo(true); |
||||
assertThat(filter(where("arr-empty").notEmpty()).apply(json, conf)).isEqualTo(false); |
||||
|
||||
assertThat(filter(where("null-key").notEmpty()).apply(json, conf)).isEqualTo(false); |
||||
} |
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// MATCHES
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
@Test |
||||
public void matches_evals() { |
||||
Predicate p = new Predicate() { |
||||
@Override |
||||
public boolean apply(Object target, Configuration configuration) { |
||||
Map<String, Object> t = (Map<String, Object>) target; |
||||
|
||||
Object o = t.get("int-key"); |
||||
|
||||
Integer i = (Integer) o; |
||||
|
||||
return i == 1; |
||||
} |
||||
}; |
||||
assertThat(filter(where("string-key").eq("string").and("$").matches(p)).apply(json, conf)).isEqualTo(true); |
||||
} |
||||
} |
@ -1,101 +0,0 @@
|
||||
package com.jayway.jsonpath; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import static junit.framework.Assert.assertEquals; |
||||
|
||||
/** |
||||
* Created by IntelliJ IDEA. |
||||
* User: kallestenflo |
||||
* Date: 3/14/12 |
||||
* Time: 7:30 AM |
||||
*/ |
||||
public class JsonModelChainedCallsTest { |
||||
|
||||
public final static String DOCUMENT = |
||||
"{ \"store\": {\n" + |
||||
" \"book\": [ \n" + |
||||
" { \"category\": \"reference\",\n" + |
||||
" \"author\": \"Nigel Rees\",\n" + |
||||
" \"title\": \"Sayings of the Century\",\n" + |
||||
" \"price\": 8.95\n" + |
||||
" },\n" + |
||||
" { \"category\": \"fiction\",\n" + |
||||
" \"author\": \"Evelyn Waugh\",\n" + |
||||
" \"title\": \"Sword of Honour\",\n" + |
||||
" \"price\": 12.99\n" + |
||||
" },\n" + |
||||
" { \"category\": \"fiction\",\n" + |
||||
" \"author\": \"Herman Melville\",\n" + |
||||
" \"title\": \"Moby Dick\",\n" + |
||||
" \"isbn\": \"0-553-21311-3\",\n" + |
||||
" \"price\": 8.99\n" + |
||||
" },\n" + |
||||
" { \"category\": \"fiction\",\n" + |
||||
" \"author\": \"J. R. R. Tolkien\",\n" + |
||||
" \"title\": \"The Lord of the Rings\",\n" + |
||||
" \"isbn\": \"0-395-19395-8\",\n" + |
||||
" \"price\": 22.99\n" + |
||||
" }\n" + |
||||
" ],\n" + |
||||
" \"bicycle\": {\n" + |
||||
" \"color\": \"red\",\n" + |
||||
" \"price\": 19.95,\n" + |
||||
" \"foo:bar\": \"fooBar\",\n" + |
||||
" \"number\": 12,\n" + |
||||
" \"dot.notation\": \"new\"\n" + |
||||
" }\n" + |
||||
" }\n" + |
||||
"}"; |
||||
|
||||
|
||||
@Test |
||||
public void convert_and_map() throws Exception { |
||||
|
||||
JsonModel model = JsonModel.model(DOCUMENT); |
||||
|
||||
Transformer<Map<String, Object>> transformer = new Transformer<Map<String, Object>>() { |
||||
public Object transform(Map<String, Object> map, Configuration configuration) { |
||||
map.remove("isbn"); |
||||
map.put("author", "kalle"); |
||||
return map; |
||||
} |
||||
}; |
||||
|
||||
Book book = model.opsForObject("store.book[0]").transform(transformer).to(Book.class); |
||||
|
||||
assertEquals(book.author, "kalle"); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void convert_each_and_map() throws Exception { |
||||
|
||||
JsonModel model = JsonModel.model(DOCUMENT); |
||||
|
||||
Transformer<Object> transformer = new Transformer<Object>() { |
||||
public Object transform(Object obj, Configuration configuration) { |
||||
Map<String, Object> map = (Map<String, Object>) obj; |
||||
map.remove("isbn"); |
||||
map.put("author", "kalle"); |
||||
return map; |
||||
} |
||||
}; |
||||
|
||||
List<Book> books = model.opsForArray("store.book").each(transformer).toList().of(Book.class); |
||||
|
||||
System.out.println(); |
||||
} |
||||
|
||||
|
||||
public static class Book { |
||||
public String category; |
||||
public String author; |
||||
public String title; |
||||
public Double price; |
||||
} |
||||
|
||||
} |
@ -1,143 +0,0 @@
|
||||
package com.jayway.jsonpath; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
import java.util.List; |
||||
import java.util.Set; |
||||
|
||||
import static com.jayway.jsonpath.Criteria.where; |
||||
import static com.jayway.jsonpath.Filter.filter; |
||||
import static com.jayway.jsonpath.JsonModel.model; |
||||
import static junit.framework.Assert.assertEquals; |
||||
|
||||
/** |
||||
* Created by IntelliJ IDEA. |
||||
* User: kallestenflo |
||||
* Date: 3/4/12 |
||||
* Time: 8:36 PM |
||||
*/ |
||||
public class JsonModelMappingTest { |
||||
|
||||
public final static String DOCUMENT = |
||||
"{ \"store\": {\n" + |
||||
" \"book\": [ \n" + |
||||
" { \"category\": \"reference\",\n" + |
||||
" \"author\": \"Nigel Rees\",\n" + |
||||
" \"title\": \"Sayings of the Century\",\n" + |
||||
" \"price\": 8.95\n" + |
||||
" },\n" + |
||||
" { \"category\": \"fiction\",\n" + |
||||
" \"author\": \"Evelyn Waugh\",\n" + |
||||
" \"title\": \"Sword of Honour\",\n" + |
||||
" \"price\": 12.99\n" + |
||||
" },\n" + |
||||
" { \"category\": \"fiction\",\n" + |
||||
" \"author\": \"Herman Melville\",\n" + |
||||
" \"title\": \"Moby Dick\",\n" + |
||||
" \"isbn\": \"0-553-21311-3\",\n" + |
||||
" \"price\": 8.99\n" + |
||||
" },\n" + |
||||
" { \"category\": \"fiction\",\n" + |
||||
" \"author\": \"J. R. R. Tolkien\",\n" + |
||||
" \"title\": \"The Lord of the Rings\",\n" + |
||||
" \"isbn\": \"0-395-19395-8\",\n" + |
||||
" \"price\": 22.99\n" + |
||||
" }\n" + |
||||
" ],\n" + |
||||
" \"bicycle\": {\n" + |
||||
" \"color\": \"red\",\n" + |
||||
" \"price\": 19.95,\n" + |
||||
" \"foo:bar\": \"fooBar\",\n" + |
||||
" \"dot.notation\": \"new\"\n" + |
||||
" }\n" + |
||||
" }\n" + |
||||
"}"; |
||||
|
||||
|
||||
@Test |
||||
public void map_and_filter_can_be_combined() throws Exception { |
||||
|
||||
|
||||
JsonModel model = JsonModel.model(DOCUMENT); |
||||
|
||||
Filter filter = Filter.filter(Criteria.where("category").is("fiction").and("price").gt(10D)); |
||||
|
||||
List<Book> books = model.map("$.store.book[?]", filter).toList().of(Book.class); |
||||
|
||||
|
||||
JsonPath jsonPath = JsonPath.compile("$.store.book[?]", filter(where("category").is("fiction").and("price").gt(10D))); |
||||
|
||||
Object read = jsonPath.read(DOCUMENT); |
||||
|
||||
List<Book> books1 = JsonModel.model(DOCUMENT).map("$.store.book[?]", filter).toListOf(Book.class); |
||||
|
||||
model(DOCUMENT); |
||||
|
||||
|
||||
System.out.println(""); |
||||
|
||||
} |
||||
|
||||
@Test |
||||
public void map_a_json_model_to_an_Class() throws Exception { |
||||
|
||||
JsonModel model = JsonModel.model(DOCUMENT); |
||||
|
||||
Book book = model.map("$.store.book[1]").to(Book.class); |
||||
|
||||
assertEquals("fiction", book.category); |
||||
assertEquals("Evelyn Waugh", book.author); |
||||
assertEquals("Sword of Honour", book.title); |
||||
assertEquals(12.99D, book.price); |
||||
} |
||||
|
||||
@Test |
||||
public void map_a_json_model_to_a_List() throws Exception { |
||||
JsonModel model = JsonModel.model(DOCUMENT); |
||||
|
||||
List<Book> booksList = model.map("$.store.book[0,1]").toListOf(Book.class); |
||||
|
||||
assertEquals("fiction", booksList.get(1).category); |
||||
assertEquals("Evelyn Waugh", booksList.get(1).author); |
||||
assertEquals("Sword of Honour", booksList.get(1).title); |
||||
assertEquals(12.99D, booksList.get(1).price); |
||||
|
||||
booksList = model.map("$.store.book[*]").toListOf(Book.class); |
||||
|
||||
assertEquals("fiction", booksList.get(1).category); |
||||
assertEquals("Evelyn Waugh", booksList.get(1).author); |
||||
assertEquals("Sword of Honour", booksList.get(1).title); |
||||
assertEquals(12.99D, booksList.get(1).price); |
||||
|
||||
booksList = model.map("$.store.book[*]").toList().of(Book.class); |
||||
|
||||
assertEquals("fiction", booksList.get(1).category); |
||||
assertEquals("Evelyn Waugh", booksList.get(1).author); |
||||
assertEquals("Sword of Honour", booksList.get(1).title); |
||||
assertEquals(12.99D, booksList.get(1).price); |
||||
} |
||||
|
||||
@Test |
||||
public void map_a_json_model_to_a_Set() throws Exception { |
||||
|
||||
JsonModel model = JsonModel.model(DOCUMENT); |
||||
|
||||
Set<Book> bookSet = model.map("$.store.book[1]").toSetOf(Book.class); |
||||
|
||||
Book book = bookSet.iterator().next(); |
||||
|
||||
assertEquals("fiction", book.category); |
||||
assertEquals("Evelyn Waugh", book.author); |
||||
assertEquals("Sword of Honour", book.title); |
||||
assertEquals(12.99D, book.price); |
||||
} |
||||
|
||||
|
||||
public static class Book { |
||||
public String category; |
||||
public String author; |
||||
public String title; |
||||
public String isbn; |
||||
public Double price; |
||||
} |
||||
} |
@ -1,286 +0,0 @@
|
||||
package com.jayway.jsonpath; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
import java.util.*; |
||||
|
||||
import static com.jayway.jsonpath.JsonModel.model; |
||||
import static junit.framework.Assert.assertEquals; |
||||
|
||||
/** |
||||
* Created by IntelliJ IDEA. |
||||
* User: kallestenflo |
||||
* Date: 3/4/12 |
||||
* Time: 4:55 PM |
||||
*/ |
||||
public class JsonModelOpsTest { |
||||
|
||||
public final static String DOCUMENT = |
||||
"{ \"store\": {\n" + |
||||
" \"book\": [ \n" + |
||||
" { \"category\": \"reference\",\n" + |
||||
" \"author\": \"Nigel Rees\",\n" + |
||||
" \"title\": \"Sayings of the Century\",\n" + |
||||
" \"price\": 8.95\n" + |
||||
" },\n" + |
||||
" { \"category\": \"fiction\",\n" + |
||||
" \"author\": \"Evelyn Waugh\",\n" + |
||||
" \"title\": \"Sword of Honour\",\n" + |
||||
" \"price\": 12.99\n" + |
||||
" },\n" + |
||||
" { \"category\": \"fiction\",\n" + |
||||
" \"author\": \"Herman Melville\",\n" + |
||||
" \"title\": \"Moby Dick\",\n" + |
||||
" \"isbn\": \"0-553-21311-3\",\n" + |
||||
" \"price\": 8.99\n" + |
||||
" },\n" + |
||||
" { \"category\": \"fiction\",\n" + |
||||
" \"author\": \"J. R. R. Tolkien\",\n" + |
||||
" \"title\": \"The Lord of the Rings\",\n" + |
||||
" \"isbn\": \"0-395-19395-8\",\n" + |
||||
" \"price\": 22.99\n" + |
||||
" }\n" + |
||||
" ],\n" + |
||||
" \"bicycle\": {\n" + |
||||
" \"color\": \"red\",\n" + |
||||
" \"price\": 19.95,\n" + |
||||
" \"foo:bar\": \"fooBar\",\n" + |
||||
" \"number\": 12,\n" + |
||||
" \"dot.notation\": \"new\"\n" + |
||||
" }\n" + |
||||
" }\n" + |
||||
"}"; |
||||
|
||||
@Test |
||||
public void convert_values() throws Exception { |
||||
|
||||
JsonModel model = model(DOCUMENT).getSubModel("store.bicycle"); |
||||
|
||||
JsonModel.ObjectOps ops = model.opsForObject(); |
||||
|
||||
assertEquals(19.95D, ops.getDouble("price")); |
||||
assertEquals(new Long(12), ops.getLong("number")); |
||||
assertEquals(new Integer(12), ops.getInteger("number")); |
||||
|
||||
int i = ops.getInteger("number"); |
||||
long l = ops.getLong("number"); |
||||
double d = ops.getDouble("price"); |
||||
} |
||||
|
||||
@Test |
||||
public void object_ops_can_update() throws Exception { |
||||
|
||||
JsonModel model = JsonModel.model(DOCUMENT); |
||||
|
||||
model.opsForObject("store.book[0]") |
||||
.put("author", "Kalle") |
||||
.put("price", 12.30D); |
||||
|
||||
assertEquals("Kalle", model.get("store.book[0].author")); |
||||
assertEquals(12.30D, model.get("store.book[0].price")); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void array_ops_can_add_element() throws Exception { |
||||
JsonModel model = JsonModel.model(DOCUMENT); |
||||
|
||||
Map<String, Object> newBook = new HashMap<String, Object>(); |
||||
newBook.put("category", "reference"); |
||||
newBook.put("author", "Kalle"); |
||||
newBook.put("title", "JSONPath book"); |
||||
newBook.put("isbn", "0-553-21311-34"); |
||||
newBook.put("price", 12.10D); |
||||
|
||||
model.opsForArray("store.book").add(newBook); |
||||
|
||||
JsonModel subModel = model.getSubModel("store.book[4]"); |
||||
|
||||
assertEquals("reference", subModel.get("category")); |
||||
assertEquals("Kalle", subModel.get("author")); |
||||
assertEquals("JSONPath book", subModel.get("title")); |
||||
assertEquals("0-553-21311-34", subModel.get("isbn")); |
||||
assertEquals(12.10D, subModel.get("price")); |
||||
} |
||||
|
||||
@Test |
||||
public void ops_can_transform_object_root() throws Exception { |
||||
|
||||
Map<String, Object> rootDocument = new HashMap<String, Object>(); |
||||
rootDocument.put("category", "reference"); |
||||
rootDocument.put("author", "Kalle"); |
||||
rootDocument.put("title", "JSONPath book"); |
||||
rootDocument.put("isbn", "0-553-21311-34"); |
||||
rootDocument.put("price", 12.10D); |
||||
|
||||
JsonModel model = JsonModel.model(rootDocument); |
||||
|
||||
model.opsForObject().transform(new Transformer<Map<String, Object>>() { |
||||
@Override |
||||
public Object transform(Map<String, Object> obj, Configuration configuration) { |
||||
obj.put("name", "kalle"); |
||||
return obj; |
||||
} |
||||
}); |
||||
assertEquals("kalle", model.get("name")); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void ops_can_transform_array_root() throws Exception { |
||||
|
||||
List<Object> rootDocument = new ArrayList<Object>(); |
||||
rootDocument.add(Collections.singletonMap("name", "kalle")); |
||||
rootDocument.add(Collections.singletonMap("name", "bob")); |
||||
rootDocument.add(Collections.singletonMap("name", "zak")); |
||||
|
||||
JsonModel model = JsonModel.model(rootDocument); |
||||
|
||||
model.opsForArray().transform(new Transformer<List<Object>>() { |
||||
@Override |
||||
public Object transform(List<Object> obj, Configuration configuration) { |
||||
return Collections.singletonMap("root", "new"); |
||||
} |
||||
}); |
||||
assertEquals("new", model.get("root")); |
||||
} |
||||
|
||||
@Test |
||||
public void ops_can_transform_nested_document() throws Exception { |
||||
|
||||
Map<String, Object> childDocument = new HashMap<String, Object>(); |
||||
childDocument.put("level", 1); |
||||
|
||||
Map<String, Object> rootDocument = new HashMap<String, Object>(); |
||||
rootDocument.put("category", "reference"); |
||||
rootDocument.put("author", "Kalle"); |
||||
rootDocument.put("title", "JSONPath book"); |
||||
rootDocument.put("isbn", "0-553-21311-34"); |
||||
rootDocument.put("price", 12.10D); |
||||
rootDocument.put("child", childDocument); |
||||
|
||||
JsonModel model = JsonModel.model(rootDocument); |
||||
|
||||
model.opsForObject("child").transform(new Transformer<Map<String, Object>>() { |
||||
@Override |
||||
public Object transform(Map<String, Object> obj, Configuration configuration) { |
||||
obj.put("name", "kalle"); |
||||
return obj; |
||||
} |
||||
}); |
||||
|
||||
assertEquals("kalle", model.get("child.name")); |
||||
} |
||||
|
||||
@Test |
||||
public void arrays_can_be_mapped() throws Exception { |
||||
JsonModel model = JsonModel.model(DOCUMENT); |
||||
|
||||
List<Book> books1 = model.opsForArray("store.book").toList().of(Book.class); |
||||
List<Book> books2 = model.opsForArray("store.book").toListOf(Book.class); |
||||
Set<Book> books3 = model.opsForArray("store.book").toSetOf(Book.class); |
||||
|
||||
assertEquals(4, books1.size()); |
||||
assertEquals(4, books2.size()); |
||||
assertEquals(4, books3.size()); |
||||
} |
||||
|
||||
@Test |
||||
public void objects_can_be_mapped() throws Exception { |
||||
JsonModel model = JsonModel.model(DOCUMENT); |
||||
|
||||
Book book = model.opsForObject("store.book[1]").to(Book.class); |
||||
|
||||
assertEquals("fiction", book.category); |
||||
assertEquals("Evelyn Waugh", book.author); |
||||
assertEquals("Sword of Honour", book.title); |
||||
assertEquals(12.99D, book.price); |
||||
|
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void object_can_be_transformed() throws Exception { |
||||
|
||||
Transformer transformer = new Transformer<Map<String, Object>>() { |
||||
@Override |
||||
public Map<String, Object> transform(Map<String, Object> model, Configuration configuration) { |
||||
model.put("newProp", "newProp"); |
||||
return model; |
||||
} |
||||
}; |
||||
|
||||
JsonModel model = JsonModel.model(DOCUMENT); |
||||
|
||||
model.opsForObject("store.book[1]").transform(transformer); |
||||
|
||||
assertEquals("newProp", model.get("store.book[1].newProp")); |
||||
} |
||||
|
||||
@Test |
||||
public void arrays_can_be_transformed() throws Exception { |
||||
Transformer transformer = new Transformer<List<Object>>() { |
||||
@Override |
||||
public Object transform(List<Object> model, Configuration configuration) { |
||||
|
||||
for (Object o : model) { |
||||
Map<String, Object> map = (Map<String, Object>) o; |
||||
map.put("newProp", "newProp"); |
||||
} |
||||
|
||||
return model; |
||||
} |
||||
}; |
||||
|
||||
JsonModel model = JsonModel.model(DOCUMENT); |
||||
|
||||
model.opsForArray("store.book").transform(transformer); |
||||
|
||||
assertEquals("newProp", model.get("store.book[1].newProp")); |
||||
} |
||||
|
||||
@Test |
||||
public void array_can_be_transformed_to_primitives() throws Exception { |
||||
Transformer positionTransformer = new Transformer<List<Object>>() { |
||||
private int i = 0; |
||||
|
||||
@Override |
||||
public Object transform(List<Object> model, Configuration configuration) { |
||||
List<Object> newList = new ArrayList<Object>(); |
||||
|
||||
for (Object o : model) { |
||||
newList.add(new Integer(i++)); |
||||
} |
||||
|
||||
return newList; |
||||
} |
||||
}; |
||||
|
||||
Transformer multiplyingTransformer = new Transformer<List<Object>>() { |
||||
@Override |
||||
public Object transform(List<Object> model, Configuration configuration) { |
||||
|
||||
for (int i = 0; i < model.size(); i++) { |
||||
int curr = (Integer) model.get(i); |
||||
model.set(i, curr * 2); |
||||
} |
||||
return model; |
||||
} |
||||
}; |
||||
|
||||
|
||||
JsonModel model = JsonModel.model(DOCUMENT); |
||||
|
||||
model.opsForArray("store.book").transform(positionTransformer).transform(multiplyingTransformer); |
||||
|
||||
assertEquals(2, model.get("store.book[1]")); |
||||
} |
||||
|
||||
public static class Book { |
||||
public String category; |
||||
public String author; |
||||
public String title; |
||||
public String isbn; |
||||
public Double price; |
||||
} |
||||
} |
@ -1,116 +0,0 @@
|
||||
package com.jayway.jsonpath; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.Map; |
||||
|
||||
import static junit.framework.Assert.assertEquals; |
||||
|
||||
/** |
||||
* Created by IntelliJ IDEA. |
||||
* User: kallestenflo |
||||
* Date: 3/11/12 |
||||
* Time: 5:07 PM |
||||
*/ |
||||
public class JsonModelSubModelTest { |
||||
|
||||
|
||||
public final static String DOCUMENT = |
||||
"{ \"store\": {\n" + |
||||
" \"book\": [ \n" + |
||||
" { \"category\": \"reference\",\n" + |
||||
" \"author\": \"Nigel Rees\",\n" + |
||||
" \"title\": \"Sayings of the Century\",\n" + |
||||
" \"price\": 8.95\n" + |
||||
" },\n" + |
||||
" { \"category\": \"fiction\",\n" + |
||||
" \"author\": \"Evelyn Waugh\",\n" + |
||||
" \"title\": \"Sword of Honour\",\n" + |
||||
" \"price\": 12.99\n" + |
||||
" },\n" + |
||||
" { \"category\": \"fiction\",\n" + |
||||
" \"author\": \"Herman Melville\",\n" + |
||||
" \"title\": \"Moby Dick\",\n" + |
||||
" \"isbn\": \"0-553-21311-3\",\n" + |
||||
" \"price\": 8.99\n" + |
||||
" },\n" + |
||||
" { \"category\": \"fiction\",\n" + |
||||
" \"author\": \"J. R. R. Tolkien\",\n" + |
||||
" \"title\": \"The Lord of the Rings\",\n" + |
||||
" \"isbn\": \"0-395-19395-8\",\n" + |
||||
" \"price\": 22.99\n" + |
||||
" }\n" + |
||||
" ],\n" + |
||||
" \"bicycle\": {\n" + |
||||
" \"color\": \"red\",\n" + |
||||
" \"price\": 19.95,\n" + |
||||
" \"foo:bar\": \"fooBar\",\n" + |
||||
" \"dot.notation\": \"new\",\n" + |
||||
" \"book\": { \"category\": \"fiction\",\n" + |
||||
" \"author\": \"J. R. R. Tolkien\",\n" + |
||||
" \"title\": \"The Lord of the Rings\",\n" + |
||||
" \"isbn\": \"0-395-19395-8\",\n" + |
||||
" \"price\": 22.99\n" + |
||||
" }\n" + |
||||
" }\n" + |
||||
" }\n" + |
||||
" }\n" + |
||||
"}"; |
||||
|
||||
|
||||
@Test(expected = IllegalArgumentException.class) |
||||
public void a_sub_model_path_must_be_definite() throws Exception { |
||||
JsonModel.model(DOCUMENT).getSubModel("$store.book[*]"); |
||||
} |
||||
|
||||
@Test |
||||
public void test_a_sub_model_can_be_fetched_and_read() throws Exception { |
||||
JsonModel model = JsonModel.model(DOCUMENT); |
||||
assertEquals("Nigel Rees", model.getSubModel("$store.book[0]").get("author")); |
||||
assertEquals("Nigel Rees", model.getSubModel(JsonPath.compile("$store.book[0]")).get("author")); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void when_a_sub_model_is_updated_the_master_model_is_updated() throws Exception { |
||||
|
||||
JsonModel model = JsonModel.model(DOCUMENT); |
||||
|
||||
JsonModel subModel = model.getSubModel("store.book[0]"); |
||||
subModel.opsForObject().put("author", "kalle"); |
||||
|
||||
assertEquals("kalle", model.get("store.book[0].author")); |
||||
} |
||||
|
||||
@Test |
||||
public void when_a_sub_model_root_is_transformed_the_master_model_is_updated() throws Exception { |
||||
|
||||
JsonModel model = JsonModel.model(DOCUMENT); |
||||
|
||||
JsonModel subModel = model.getSubModel("store.book[0]"); |
||||
subModel.opsForObject().transform(new Transformer<Map<String, Object>>() { |
||||
@Override |
||||
public Object transform(Map<String, Object> obj, Configuration configuration) { |
||||
return Collections.singletonMap("prop", "new"); |
||||
} |
||||
}); |
||||
assertEquals("new", model.get("store.book[0].prop")); |
||||
} |
||||
|
||||
@Test |
||||
public void when_a_sub_model_child_is_transformed_the_master_model_is_updated() throws Exception { |
||||
|
||||
JsonModel model = JsonModel.model(DOCUMENT); |
||||
|
||||
JsonModel subModel = model.getSubModel("store.bicycle.book"); |
||||
subModel.opsForObject().transform(new Transformer<Map<String, Object>>() { |
||||
@Override |
||||
public Object transform(Map<String, Object> obj, Configuration configuration) { |
||||
return Collections.singletonMap("prop", "new"); |
||||
} |
||||
}); |
||||
assertEquals("new", model.get("store.bicycle.book.prop")); |
||||
} |
||||
|
||||
} |
@ -1,135 +0,0 @@
|
||||
package com.jayway.jsonpath; |
||||
|
||||
import org.junit.Ignore; |
||||
import org.junit.Test; |
||||
|
||||
import java.io.ByteArrayInputStream; |
||||
import java.net.URL; |
||||
import java.util.Collections; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
import static java.util.Arrays.asList; |
||||
import static junit.framework.Assert.*; |
||||
|
||||
/** |
||||
* Created by IntelliJ IDEA. |
||||
* User: kallestenflo |
||||
* Date: 11/8/11 |
||||
* Time: 7:52 PM |
||||
*/ |
||||
public class JsonModelTest { |
||||
|
||||
public final static String DOCUMENT = |
||||
"{ \"store\": {\n" + |
||||
" \"book\": [ \n" + |
||||
" { \"category\": \"reference\",\n" + |
||||
" \"author\": \"Nigel Rees\",\n" + |
||||
" \"title\": \"Sayings of the Century\",\n" + |
||||
" \"price\": 8.95\n" + |
||||
" },\n" + |
||||
" { \"category\": \"fiction\",\n" + |
||||
" \"author\": \"Evelyn Waugh\",\n" + |
||||
" \"title\": \"Sword of Honour\",\n" + |
||||
" \"price\": 12.99\n" + |
||||
" },\n" + |
||||
" { \"category\": \"fiction\",\n" + |
||||
" \"author\": \"Herman Melville\",\n" + |
||||
" \"title\": \"Moby Dick\",\n" + |
||||
" \"isbn\": \"0-553-21311-3\",\n" + |
||||
" \"price\": 8.99\n" + |
||||
" },\n" + |
||||
" { \"category\": \"fiction\",\n" + |
||||
" \"author\": \"J. R. R. Tolkien\",\n" + |
||||
" \"title\": \"The Lord of the Rings\",\n" + |
||||
" \"isbn\": \"0-395-19395-8\",\n" + |
||||
" \"price\": 22.99\n" + |
||||
" }\n" + |
||||
" ],\n" + |
||||
" \"bicycle\": {\n" + |
||||
" \"color\": \"red\",\n" + |
||||
" \"price\": 19.95,\n" + |
||||
" \"foo:bar\": \"fooBar\",\n" + |
||||
" \"dot.notation\": \"new\"\n" + |
||||
" }\n" + |
||||
" }\n" + |
||||
"}"; |
||||
|
||||
public final static String INVALID_DOCUMENT = "{?\\?\\?!!?~q`}}}}}\"\" \"store\": {\n"; |
||||
|
||||
@Test(expected = InvalidJsonException.class) |
||||
public void invalid_json_throws() throws Exception { |
||||
JsonModel.model(INVALID_DOCUMENT).get("store.id"); |
||||
} |
||||
|
||||
@Test(expected = InvalidPathException.class) |
||||
public void invalid_path_throws() throws Exception { |
||||
JsonModel.model(DOCUMENT).get("a("); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void a_model_can_be_pretty_printed() throws Exception { |
||||
JsonModel model = JsonModel.model(DOCUMENT); |
||||
|
||||
model.print(); |
||||
} |
||||
|
||||
@Test |
||||
public void has_path_validates() throws Exception { |
||||
assertFalse(JsonModel.model(DOCUMENT).hasPath("store.invalid")); |
||||
assertFalse(JsonModel.model(DOCUMENT).hasPath("store.book[0].foo")); |
||||
|
||||
assertTrue(JsonModel.model(DOCUMENT).hasPath("store.book")); |
||||
assertTrue(JsonModel.model(DOCUMENT).hasPath("store.book[0].title")); |
||||
} |
||||
|
||||
@Test |
||||
public void a_json_document_can_be_fetched_with_a_URL() throws Exception { |
||||
URL url = new URL("http://maps.googleapis.com/maps/api/geocode/json"); |
||||
assertEquals("REQUEST_DENIED", JsonModel.model(url).get("status")); |
||||
} |
||||
|
||||
@Test |
||||
public void a_json_document_can_be_fetched_with_a_InputStream() throws Exception { |
||||
ByteArrayInputStream bis = new ByteArrayInputStream(DOCUMENT.getBytes()); |
||||
assertEquals("Nigel Rees", JsonModel.model(bis).get("store.book[0].author")); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void maps_and_list_can_queried() throws Exception { |
||||
Map<String, Object> doc = new HashMap<String, Object>(); |
||||
doc.put("items", asList(0, 1, 2)); |
||||
doc.put("child", Collections.singletonMap("key", "value")); |
||||
|
||||
JsonModel model = JsonModel.model(doc); |
||||
|
||||
assertEquals("value", model.get("$child.key")); |
||||
assertEquals(1, model.get("$items[1]")); |
||||
assertEquals("{\"child\":{\"key\":\"value\"},\"items\":[0,1,2]}", model.toJson()); |
||||
} |
||||
|
||||
|
||||
|
||||
@Test |
||||
public void query_for_null_property_returns_null() { |
||||
String documentWithNull = |
||||
"{ \"store\": {\n" + |
||||
" \"book\": { \n" + |
||||
" \"color\": null\n" + |
||||
" }\n" + |
||||
" }\n" + |
||||
"}"; |
||||
|
||||
Object color = JsonModel.model(documentWithNull).get("store.book.color"); |
||||
|
||||
assertNull(color); |
||||
} |
||||
|
||||
@Test(expected = InvalidPathException.class) |
||||
public void query_for_property_on_array_throws() throws Exception { |
||||
JsonModel.model(DOCUMENT).get("store.book.color"); |
||||
} |
||||
|
||||
} |
@ -1,156 +0,0 @@
|
||||
package com.jayway.jsonpath; |
||||
|
||||
import com.jayway.jsonpath.internal.PathTokenizer; |
||||
import org.hamcrest.Matcher; |
||||
import org.junit.Test; |
||||
|
||||
import static org.hamcrest.Matchers.hasItems; |
||||
import static org.junit.Assert.*; |
||||
|
||||
/** |
||||
* Created by IntelliJ IDEA. |
||||
* User: kallestenflo |
||||
* Date: 2/2/11 |
||||
* Time: 1:22 PM |
||||
*/ |
||||
public class PathTest { |
||||
|
||||
Filter filter = new Filter(){ |
||||
@Override |
||||
public boolean accept(Object obj) { |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public boolean accept(Object obj, Configuration configuration) { |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public Filter addCriteria(Criteria criteria) { |
||||
return this; |
||||
} |
||||
}; |
||||
|
||||
@Test |
||||
public void path_is_not_definite() throws Exception { |
||||
assertFalse(JsonPath.compile("$..book[0]").isPathDefinite()); |
||||
assertFalse(JsonPath.compile("$book[?]", filter).isPathDefinite()); |
||||
assertFalse(JsonPath.compile("$.books[*]").isPathDefinite()); |
||||
} |
||||
|
||||
@Test |
||||
public void path_is_definite() throws Exception { |
||||
assertTrue(JsonPath.compile("$.definite.this.is").isPathDefinite()); |
||||
assertTrue(JsonPath.compile("rows[0].id").isPathDefinite()); |
||||
} |
||||
|
||||
@Test |
||||
public void valid_path_is_split_correctly() throws Exception { |
||||
|
||||
assertPath("$.store[*]", hasItems("$", "store", "[*]")); |
||||
|
||||
assertPath("$", hasItems("$")); |
||||
|
||||
assertPath("$..*", hasItems("$", "..", "*")); |
||||
|
||||
assertPath("$.store", hasItems("$", "store")); |
||||
|
||||
assertPath("$.store.*", hasItems("$", "store", "*")); |
||||
|
||||
assertPath("$.store[*].name", hasItems("$", "store", "[*]", "name")); |
||||
|
||||
assertPath("$..book[-1:].foo.bar", hasItems("$", "..", "book", "[-1:]", "foo", "bar")); |
||||
|
||||
assertPath("$..book[?(@.isbn)]", hasItems("$", "..", "book", "[?(@.isbn)]")); |
||||
|
||||
assertPath("['store'].['price']", hasItems("$", "store", "price")); |
||||
|
||||
assertPath("$.['store'].['price']", hasItems("$", "store", "price")); |
||||
|
||||
assertPath("$.['store']['price']", hasItems("$", "store", "price")); |
||||
|
||||
assertPath("$.['store'].price", hasItems("$", "store", "price")); |
||||
|
||||
assertPath("$.['store space']['price space']", hasItems("$", "store space", "price space")); |
||||
|
||||
assertPath("$.['store']['nice.price']", hasItems("$", "store", "nice.price")); |
||||
|
||||
assertPath("$..book[?(@.price<10)]", hasItems("$", "..", "book", "[?(@.price<10)]")); |
||||
|
||||
assertPath("$..book[?(@.price<10)]", hasItems("$", "..", "book", "[?(@.price<10)]")); |
||||
|
||||
assertPath("$.store.book[*].author", hasItems("$", "store", "book", "[*]", "author")); |
||||
|
||||
assertPath("$.store..price", hasItems("$", "store", "..", "price")); |
||||
|
||||
} |
||||
|
||||
@Test |
||||
public void white_space_are_removed() throws Exception { |
||||
|
||||
assertPath("$.[ 'store' ]", hasItems("$", "store")); |
||||
|
||||
assertPath("$.[ 'store' ]", hasItems("$", "store")); |
||||
|
||||
assertPath("$.['store bore']", hasItems("$", "store bore")); |
||||
|
||||
assertPath("$..book[ ?(@.price<10) ]", hasItems("$", "..", "book", "[?(@.price<10)]")); |
||||
|
||||
assertPath("$..book[?(@.price<10 )]", hasItems("$", "..", "book", "[?(@.price<10)]")); |
||||
|
||||
assertPath("$..book[?( @.price<10)]", hasItems("$", "..", "book", "[?(@.price<10)]")); |
||||
|
||||
assertPath("$..book[ ?(@.price<10)]", hasItems("$", "..", "book", "[?(@.price<10)]")); |
||||
} |
||||
|
||||
@Test |
||||
public void dot_ending_ignored() throws Exception { |
||||
|
||||
assertPath("$..book['something'].", hasItems("$", "..", "something")); |
||||
|
||||
} |
||||
|
||||
@Test |
||||
public void invalid_path_throws_exception() throws Exception { |
||||
assertPathInvalid("$...*"); |
||||
} |
||||
|
||||
@Test |
||||
public void multi_field_select(){ |
||||
PathTokenizer tokenizer = new PathTokenizer("$.contents[*].['groupType', 'type']"); |
||||
for (String fragment : tokenizer.getFragments()) { |
||||
System.out.println(fragment); |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
//----------------------------------------------------------------
|
||||
|
||||
private void assertPathInvalid(String path) { |
||||
try { |
||||
PathTokenizer tokenizer = new PathTokenizer(path); |
||||
assertTrue("Expected exception!", false); |
||||
} catch (InvalidPathException expected) {} |
||||
} |
||||
|
||||
private void assertPath(String path, Matcher<Iterable<String>> matcher) { |
||||
System.out.println("PATH: " + path); |
||||
|
||||
PathTokenizer tokenizer = new PathTokenizer(path); |
||||
|
||||
for (String fragment : tokenizer.getFragments()) { |
||||
System.out.println(fragment); |
||||
} |
||||
|
||||
assertThat(tokenizer.getFragments(), matcher); |
||||
System.out.println("----------------------------------"); |
||||
} |
||||
|
||||
|
||||
} |
@ -1,4 +1,4 @@
|
||||
package com.jayway.jsonpath.internal.filter; |
||||
package com.jayway.jsonpath.internal; |
||||
|
||||
import com.jayway.jsonpath.JsonPath; |
||||
import org.hamcrest.Matchers; |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue