Giter Club home page Giter Club logo

jsonsurfer's Introduction

JsonSurfer - Let's surf on Json!

Join the chat at https://gitter.im/jsurfer/JsonSurfer build Coverage Status Maven Central

Why JsonSurfer

  • Streaming

    No need to deserialize entire json into memory.

  • JsonPath

    Selectively extract json data by the power of JsonPath.

  • Stoppable

    JsonSurfer is built on stoppable SAX-like interface that allows the processor to stop itself if necessary.

  • Non-Blocking

    JsonSurfer is event-driven and offers non-blocking parser interface.

  • Binary format

    Support multiple binary data formats including Avro, CBOR, Protobuf, Smile and Ion.

Getting started

  • Supported JsonPath operator in JsonSurfer:
Operator Description
$ root
@ current node
* wildcard
.. recursive descent
.<name> child
['<name>' (, '<name>')] child/children
[<number> (, <number>)] index/indices
[start:end] array slice
[?(<expression>)] filter expression
  • JsonSurfer is available in cetral maven repository.

JsonSurfer has drivers for most of popular json libraries including: Gson, Jackson, FastJson and JsonSimple. Choose one and add to your POM.

<dependency>
    <groupId>com.github.jsurfer</groupId>
    <artifactId>jsurfer-gson</artifactId>
    <version>1.6.3</version>
</dependency>

<dependency>
    <groupId>com.github.jsurfer</groupId>
    <artifactId>jsurfer-jackson</artifactId>
    <version>1.6.3</version>
</dependency>

<dependency>
    <groupId>com.github.jsurfer</groupId>
    <artifactId>jsurfer-fastjson</artifactId>
    <version>1.6.3</version>
</dependency>

<dependency>
    <groupId>com.github.jsurfer</groupId>
    <artifactId>jsurfer-jsonsimple</artifactId>
    <version>1.6.3</version>
</dependency>

Usage:

Create your JsonSurfer:

  • JsonSurfer has flexible constructor. You can create yourself or pick a prebuilt one according the json library you used:
  1. Gson
        // use gson parser and use gson provider use to deserialize json into gson model i.e.com.google.gson.JsonElement
        JsonSurfer surfer = new JsonSurfer(GsonParser.INSTANCE, GsonProvider.INSTANCE);

or

        JsonSurfer surfer = JsonSurferGson.INSTANCE;
  1. Jackson
        JsonSurfer surfer = new JsonSurfer(JacksonParser.INSTANCE, JacksonProvider.INSTANCE);

or

        JsonSurfer surfer = JsonSurferJackson.INSTANCE;
  1. JsonSimple
        // use json-simple parser and json-simple provider to deserialize json into json-simple model i.e.org.json.simple.JSONObject or org.json.simple.JSONArray
        JsonSurfer surfer = new JsonSurfer(JsonSimpleParser.INSTANCE, JsonSimpleProvider.INSTANCE);

or

        JsonSurfer surfer = JsonSurferJsonSimple.INSTANCE;
  1. Fastjson
        JsonSurfer surfer = new JsonSurfer(FastJsonParser.INSTANCE, FastJsonProvider.INSTANCE);

or

        JsonSurfer surfer = JsonSurferFastJson.INSTANCE;

Collect value by JsonPath

        Collector collector = surfer.collector(sample);
        ValueBox<String> box1 = collector.collectOne("$.store.book[1].category", String.class);
        ValueBox<Object> box2 = collector.collectOne("$.store.book[2].isbn");
        ValueBox<Collection<Object>> box3 = collector.collectAll("$.store.book[*]");
        collector.exec(); // make sure exec() invoked before getting value from boxes
        box1.get();
        box2.get();
        box3.get();

【DEPRECATED】 Collect the first matched value and stop immediately

        JsonSurfer jsonSurfer = JsonSurferGson.INSTANCE;
        Object singleResult = jsonSurfer.collectOne(sample, "$.store.book[0]");

【DEPRECATED】 Colllect every matched value into a collection

        JsonSurfer jsonSurfer = JsonSurferGson.INSTANCE;
        Collection<Object> multipleResults = jsonSurfer.collectAll(sample, "$.store.book[*]");

"Surfing" in Json and collecting matched value in the listeners

        JsonSurfer surfer = JsonSurferGson.INSTANCE;
        surfer.configBuilder()
                .bind("$.store.book[*]", new JsonPathListener() {
                    @Override
                    public void onValue(Object value, ParsingContext context) {
                        System.out.println(value);
                    }
                })
                .buildAndSurf(sample);

Reuse listener binding.

SurfingConfiguration is thread-safe as long as your listeners are stateless.

        JsonSurfer surfer = JsonSurferGson.INSTANCE;
        SurfingConfiguration config = surfer.configBuilder()
                .bind("$.store.book[*]", new JsonPathListener() {
                    @Override
                    public void onValue(Object value, ParsingContext context) {
                        System.out.println(value);
                    }
                })
                .build();        
        surfer.surf(sample1, config);
        surfer.surf(sample2, config);

Compiled JsonPath

JsonPath object is immutable and can be reused safely.

Tips: Most of JsonSurfer API have two version: accepting raw JsonPath string or JsonPath object. The latter is always faster than the former without compiling JsonPath.

        JsonPath compiledPath = JsonPathCompiler.compile("$..book[1,3]['author','title']");
        String value = surfer.collectOne(read("sample.json"), String.class, compiledPath);

JsonPath Filters

  • Filter operators
Operator Description
== equal
< less than
> greater than

You can use logical operators '&&' and '||' to create more complex filter expression. For example:

$.store.book[?(@.price < 10 || @.category && @.isbn && @.price>10)].volumes[?(@.chapter == 1)]

Resolver API:

  • Limitation: Wildcard and Recursive Descent are NOT supported.
  • As of 1.2.6, JsonSurfer provides another way of processing json. You can directly resolve value with JsonPath from a well-built DOM like HashMap or even POJO:
        Book book = new Book();
        book.setAuthor("Leo");
        book.setCategory("Fiction");
        book.setPrice(100.0d);
        book.setTitle("JsonSurfer is great!");
        System.out.print(compile("$.author").resolve(book, new PoJoResolver()));

which prints "Leo".

        List<String> list = Arrays.asList("foo", "bar");
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("list", list);
        System.out.println(compile("$.list[1]").resolve(map, JavaCollectionProvider.INSTANCE));

which prints "bar".

  • If you want to process POJO with full JsonPath feature, you can convert the POJO into binary format and then surfer on it.

Binaray format (Jackson only)

By importing Jackson binary format backend, JsonSurfer is capable to surfer with multiple binary object representation formats such as Avro, CBOR, Protobuf(A known bug to be fixed in Jackson 2.9.6), Smile and Ion.

For example, if you want to surfer with CBOR data, firstly, CBOR format backend need to be imported as dependency.

    <dependency>
        <groupId>com.fasterxml.jackson.dataformat</groupId>
        <artifactId>jackson-dataformat-cbor</artifactId>
        <version>${jackson.version}</version>
    </dependency>

Then create a JsonSurfer with CBOR-backed JacksonParser and surfer as usual

    surfer = new JsonSurfer(new JacksonParser(new CBORFactory()), provider);

Find more examples here: https://github.com/jsurfer/JsonSurfer/blob/master/jsurfer-all/src/test/java/org/jsfr/json/JacksonParserTest.java

Share data among processors

Since JsonSurfer emit data in the way of callback, it would be difficult if one of your processing depends one another. Therefore a simple transient map is added for sharing data among your processors. Following unit test shows how to use it:

        surfer.configBuilder().bind("$.store.book[1]", new JsonPathListener() {
            @Override
            public void onValue(Object value, ParsingContext context) {
                context.save("foo", "bar");
            }
        }).bind("$.store.book[2]", new JsonPathListener() {
            @Override
            public void onValue(Object value, ParsingContext context) {
                assertEquals("bar", context.load("foo", String.class));
            }
        }).bind("$.store.book[0]", new JsonPathListener() {
            @Override
            public void onValue(Object value, ParsingContext context) {
                assertNull(context.load("foo", String.class));
            }
        }).buildAndSurf(read("sample.json"));

Control parsing

  • How to pause and resume parsing.
    SurfingConfiguration config = surfer.configBuilder()
            .bind("$.store.book[0]", new JsonPathListener() {
                @Override
                public void onValue(Object value, ParsingContext context) {
                    LOGGER.info("The first pause");
                    context.pause();
                }
            })
            .bind("$.store.book[1]", new JsonPathListener() {
                @Override
                public void onValue(Object value, ParsingContext context) {
                    LOGGER.info("The second pause");
                    context.pause();
                }
            }).build();
    ResumableParser parser = surfer.getResumableParser(read("sample.json"), config);
    assertFalse(parser.resume());
    LOGGER.info("Start parsing");
    parser.parse();
    LOGGER.info("Resume from the first pause");
    assertTrue(parser.resume());
    LOGGER.info("Resume from the second pause");
    assertTrue(parser.resume());
    LOGGER.info("Parsing stopped");
    assertFalse(parser.resume());

Java 8 Streams API support

As of 1.4, JsonSurfer can create an iterator from Json and JsonPath. Matched value can be pulled from the iterator one by one without loading entire json into memory.

    Iterator iterator = surfer.iterator(read("sample.json"), JsonPathCompiler.compile("$.store.book[*]"));

Java8 user can also convert the iterator into a Stream

    Stream<Object> targetStream = StreamSupport.stream(
          Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED),
          false);

Functions implmented by Streams API

    public static Stream<Object> toStream(String json, String path) {
        Iterator<Object> iterator = JsonSurferJackson.INSTANCE.iterator(json, JsonPathCompiler.compile(path));
        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false);
    }

    public static void main(String[] s) throws Exception {
        String json = "[1,3,5,7,11]";
        // Count
        System.out.println(toStream(json, "$[*]").count());
        // Max
        toStream(json, "$[*]").mapToInt(o -> ((LongNode) o).asInt()).max().ifPresent(System.out::println);
        // Min
        toStream(json, "$[*]").mapToInt(o -> ((LongNode) o).asInt()).min().ifPresent(System.out::println);
        // Average
        toStream(json, "$[*]").mapToDouble(o -> ((LongNode) o).asDouble()).average().ifPresent(System.out::println);
    }

Non-Blocking parsing

As of 1.4, JsonSurfer support non-blocking parsing for JacksonParser. You can achieve 100% non-blocking JSON processing with JsonSurfer in a NIO application. Let's take a Vertx request handler as an example:

    Vertx vertx = Vertx.vertx();
    HttpServer server = vertx.createHttpServer(new HttpServerOptions());
    JsonSurfer surfer = JsonSurferJackson.INSTANCE;
    SurfingConfiguration config = surfer.configBuilder()
            .bind("$[*]", (JsonPathListener) (value, context) -> {
                // Handle json
                System.out.println(value);
            }).build();
    server.requestHandler(request -> {
        NonBlockingParser parser = surfer.createNonBlockingParser(config);
        request.handler(buffer -> {
            byte[] bytes = buffer.getBytes();
            System.out.println("Received " + bytes.length + " bytes");
            parser.feed(bytes, 0, bytes.length);
        });
        request.endHandler(aVoid -> {
            parser.endOfInput();
            System.out.println("End of request");
            request.response().end();
        });
    }).listen(8080);

Examples

Sample Json:

{
    "store": {
        "book": [
            {
                "category": "reference",
                "author": "Nigel Rees",
                "title": "Sayings of the Century",
                "price": 8.95
            },
            {
                "category": "fiction",
                "author": "Evelyn Waugh",
                "title": "Sword of Honour",
                "price": 12.99
            },
            {
                "category": "fiction",
                "author": "Herman Melville",
                "title": "Moby Dick",
                "isbn": "0-553-21311-3",
                "price": 8.99
            },
            {
                "category": "fiction",
                "author": "J. R. R. Tolkien",
                "title": "The Lord of the Rings",
                "isbn": "0-395-19395-8",
                "price": 22.99
            }
        ],
        "bicycle": {
            "color": "red",
            "price": 19.95
        }
    },
    "expensive": 10
}
JsonPath Result
$.store.book[*].author Find the authors of all books
$..author All authors
$.store.* All things in store
$.store..price The price of everything in the store
$..book[2] The third book
$..book[0,1] The first two books
$.store.book[?(@.price==8.95)] Filter all books whose price equals to 8.95
$.store.book[?(@.category=='fiction')]             Filter all books which belong to fiction category                  
$.store.book[?(@.author=~/tolkien/i)] All books matching regex

Find the authors of all books:

$.store.book[*].author
        JsonSurfer surfer = JsonSurferGson.INSTANCE;
        surfer.configBuilder()
                .bind("$.store.book[*].author", new JsonPathListener() {
                    @Override
                    public void onValue(Object value, ParsingContext context) {
                        System.out.println(value);
                    }
                })
                .buildAndSurf(sample);

Output

"Nigel Rees"
"Evelyn Waugh"
"Herman Melville"
"J. R. R. Tolkien"

All authors

$..author
        JsonSurfer surfer = JsonSurferGson.INSTANCE;
        surfer.configBuilder()
                .bind("$..author", new JsonPathListener() {
                    @Override
                    public void onValue(Object value, ParsingContext context) {
                        System.out.println(value);
                    }
                })
                .buildAndSurf(sample);

Output

"Nigel Rees"
"Evelyn Waugh"
"Herman Melville"
"J. R. R. Tolkien"

All things in store

$.store.*
        JsonSurfer surfer = JsonSurferGson.INSTANCE;
        surfer.configBuilder()
                .bind("$.store.*", new JsonPathListener() {
                    @Override
                    public void onValue(Object value, ParsingContext context) {
                        System.out.println(value);
                    }
                })
                .buildAndSurf(sample);

Output

[{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}]
{"color":"red","price":19.95}

The price of everything in the store

$.store..price
        JsonSurfer surfer = JsonSurferGson.INSTANCE;
        surfer.configBuilder()
                .bind("$.store..price", new JsonPathListener() {
                    @Override
                    public void onValue(Object value, ParsingContext context) {
                        System.out.println(value);
                    }
                })
                .buildAndSurf(sample);

Output

8.95
12.99
8.99
22.99
19.95

The third book

$..book[2]
        JsonSurfer surfer = JsonSurferGson.INSTANCE;
        surfer.configBuilder()
                .bind("$..book[2]", new JsonPathListener() {
                    @Override
                    public void onValue(Object value, ParsingContext context) {
                        System.out.println(value);
                    }
                })
                .buildAndSurf(sample);

Output

{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99}

The first two books

$..book[0,1]
        JsonSurfer surfer = JsonSurferGson.INSTANCE;
        surfer.configBuilder()
                .bind("$..book[0,1]", new JsonPathListener() {
                    @Override
                    public void onValue(Object value, ParsingContext context) {
                        System.out.println(value);
                    }
                })
                .buildAndSurf(sample);

Output

{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95}
{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99}

Filter all books whose price equals to 8.95

$.store.book[?(@.price==8.95)]
        JsonSurfer surfer = JsonSurferGson.INSTANCE;
        surfer.configBuilder()
                .bind("$.store.book[?(@.price==8.95)]", new JsonPathListener() {
                    @Override
                    public void onValue(Object value, ParsingContext context) {
                        System.out.println(value);
                    }
                })
                .buildAndSurf(sample);

Output

{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95}

Filter all books which belong to fiction category

$.store.book[?(@.category=='fiction')]
        JsonSurfer surfer = JsonSurferGson.INSTANCE;
        surfer.configBuilder()
                .bind("$.store.book[?(@.category=='fiction')]", new JsonPathListener() {
                    @Override
                    public void onValue(Object value, ParsingContext context) {
                        System.out.println(value);
                    }
                })
                .buildAndSurf(sample);

Output

{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99}
{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99}
{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}

All books matching regex

$.store.book[?(@.author=~/tolkien/i)]
        JsonSurfer surfer = JsonSurferGson.INSTANCE;
        surfer.configBuilder()
                .bind("$.store.book[?(@.author=~/tolkien/i)]')]", new JsonPathListener() {
                    @Override
                    public void onValue(Object value, ParsingContext context) {
                        System.out.println(value);
                    }
                })
                .buildAndSurf(sample);

Output

{"author":"J. R. R. Tolkien","price":22.99,"isbn":"0-395-19395-8","category":"fiction","title":"The Lord of the Rings"}

Stoppable parsing

The parsing is stopped when the first book found and printed.

$..book[0,1]
        JsonSurfer surfer = JsonSurferGson.INSTANCE;
        surfer.configBuilder()
                .bind("$..book[0,1]", new JsonPathListener() {
                    @Override
                    public void onValue(Object value, ParsingContext context) {                        
                        System.out.println(value);
                        context.stop();
                    }
                })
                .buildAndSurf(sample);

Output

{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95}

Benchmark

  • JsonSurfer is fast !!! The benchmark is powered by JMH
Benchmark                                                       Mode  Cnt       Score       Error  Units
BenchmarkCollectSingleValue.benchmarkFastjson                  thrpt   10  139772.275      8854.369  ops/s
BenchmarkCollectSingleValue.benchmarkFastjsonWithJsonSurfer    thrpt   10  699176.961      23396.619  ops/s
BenchmarkCollectSingleValue.benchmarkGson                      thrpt   10  139394.358      6019.764  ops/s
BenchmarkCollectSingleValue.benchmarkGsonWithJsonSurfer        thrpt   10  632155.657      15484.499  ops/s
BenchmarkCollectSingleValue.benchmarkJackson                   thrpt   10  160545.079      7006.525  ops/s
BenchmarkCollectSingleValue.benchmarkJacksonWithJsonSurfer     thrpt   10  451870.586      13132.576  ops/s
BenchmarkCollectSingleValue.benchmarkJsonSimpleWithJsonSurfer  thrpt   10  155094.948      4457.502  ops/s

jsonsurfer's People

Contributors

chillb0nes avatar cowtowncoder avatar dependabot[bot] avatar gitter-badger avatar oleg-smith avatar slashbin avatar splatch avatar wanglingsong avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jsonsurfer's Issues

Values nested in array not supported

My use case is rather simple as I do not do any filtering but flattening of remote service response. My use case is supported by jq, but fails to get evaluated with JsonSurfer (and json path too).

My jq expression is
.entry[] | {url: .fullUrl, values: [ .resource.component[]? | .valueQuantity.value ] }

Which could be translated to json surfer:
$.entry['fullUrl', resource.component[*].valueQuantity.value]

However there are two major issues. From my evaluation it seems that ['property1', 'property2'] syntax always returns first property value and ommits second. Second issue is array wildcard path operator after which no more additional emenets may occur (althrought it passes compilation). Depending on expression variant it will cause class cast exception (usage of ['property'] syntax in context of array) or operation not supported exceptions ([*].property).

Are there any chances for support of such scenarios?

For reference I attach simplified resource model I have.

{
  "entry": [{
    "fullUrl": "http://localhost:8080/test/314",
    "resource": {
      "id": "56",
      "meta": {
        "lastUpdated": "2017-12-07T22:44:51.744+01:00"
      },
      "effectiveDateTime": "2009-09-09T00:00:00",
      "component": [{
        "valueQuantity": {
          "value": 109
        }
      },{
        "valueQuantity": {
          "value": 119
        }
      }]
    }
  },{
    "fullUrl": "http://localhost:8080/test/318",
    "resource": {
      "id": "57",
      "valueQuantity": {
        "value": 100
      }
    }
  }]
}

Output given by jq:

{
  "url": "http://localhost:8080/test/314",
  "values": [
    109,
    119
  ]
}
{
  "url": "http://localhost:8080/test/318",
  "values": []
}

(second resource does not have "component" embedded but valueQuantity directly at resource level)

Results do not match other implementations

The following queries provide results that do not match those of other implementations of JSONPath
(compare https://cburgmer.github.io/json-path-comparison/):

  • $.2
    Input:

    {"a": "first", "2": "second", "b": "third"}
    

    Expected output:

    ["second"]
    

    Error:

    Exception in thread "main" org.antlr.v4.runtime.misc.ParseCancellationException
    	at org.antlr.v4.runtime.BailErrorStrategy.recoverInline(BailErrorStrategy.java:66)
    	at org.antlr.v4.runtime.Parser.match(Parser.java:206)
    	at org.jsfr.json.compiler.JsonPathParser.childNode(JsonPathParser.java:672)
    	at org.jsfr.json.compiler.JsonPathParser.relativePath(JsonPathParser.java:272)
    	at org.jsfr.json.compiler.JsonPathParser.path(JsonPathParser.java:159)
    	at org.jsfr.json.compiler.JsonPathCompiler.compile(JsonPathCompiler.java:283)
    	at org.jsfr.json.compiler.JsonPathCompiler.compile(JsonPathCompiler.java:273)
    	at org.jsfr.json.JsonSurfer.collectAll(JsonSurfer.java:262)
    	at query.App.main(App.java:27)
    Caused by: org.antlr.v4.runtime.InputMismatchException
    	at org.antlr.v4.runtime.BailErrorStrategy.recoverInline(BailErrorStrategy.java:61)
    	... 8 more
    
  • $[-1]
    Input:

    ["first", "second", "third"]
    

    Expected output:

    ["third"]
    

    Actual output:

    []
    
  • $[-1:]
    Input:

    ["first", "second", "third"]
    

    Expected output:

    ["third"]
    

    Actual output:

    ["first", "second", "third"]
    
  • $[?(@.key=="some.value")]
    Input:

    [{"key": "some"}, {"key": "value"}, {"key": "some.value"}]
    

    Expected output:

    [{"key": "some.value"}]
    

    Error:

    line 1:11 token recognition error at: '"'
    line 1:22 token recognition error at: '"'
    line 1:12 no viable alternative at input '@.key==some'
    Exception in thread "main" org.antlr.v4.runtime.misc.ParseCancellationException
    	at org.antlr.v4.runtime.BailErrorStrategy.recover(BailErrorStrategy.java:51)
    	at org.jsfr.json.compiler.JsonPathParser.filterExpr(JsonPathParser.java:947)
    	at org.jsfr.json.compiler.JsonPathParser.filter(JsonPathParser.java:768)
    	at org.jsfr.json.compiler.JsonPathParser.relativePath(JsonPathParser.java:307)
    	at org.jsfr.json.compiler.JsonPathParser.path(JsonPathParser.java:159)
    	at org.jsfr.json.compiler.JsonPathCompiler.compile(JsonPathCompiler.java:283)
    	at org.jsfr.json.compiler.JsonPathCompiler.compile(JsonPathCompiler.java:273)
    	at org.jsfr.json.JsonSurfer.collectAll(JsonSurfer.java:262)
    	at query.App.main(App.java:27)
    Caused by: org.antlr.v4.runtime.NoViableAltException
    	at org.antlr.v4.runtime.atn.ParserATNSimulator.noViableAlt(ParserATNSimulator.java:2026)
    	at org.antlr.v4.runtime.atn.ParserATNSimulator.execATN(ParserATNSimulator.java:467)
    	at org.antlr.v4.runtime.atn.ParserATNSimulator.adaptivePredict(ParserATNSimulator.java:393)
    	at org.jsfr.json.compiler.JsonPathParser.filterExpr(JsonPathParser.java:843)
    	... 7 more
    

For reference, the output was generated by the program in https://github.com/cburgmer/json-path-comparison/tree/master/implementations/Java_com.github.jsurfer.

Get attribute value with filtering not working

Hello

I have a json structure like this
{ "policy" : { "policy-details" : { "parties" : { "party" : [{"role" : "role1", "name" : "name1"}, {"role" : "role2", "name" : "name2"}]}} } }
With this jsonPath $.policy.policy-details.parties.party[?(@.role == 'role1')].name I got
org.antlr.v4.runtime.misc.ParseCancellationException
at org.antlr.v4.runtime.BailErrorStrategy.recoverInline(BailErrorStrategy.java:66)
at org.antlr.v4.runtime.Parser.match(Parser.java:206)
at org.jsfr.json.compiler.JsonPathParser.path(JsonPathParser.java:154)
at org.jsfr.json.compiler.JsonPathCompiler.compile(JsonPathCompiler.java:238)
at org.jsfr.json.SurfingConfiguration$Builder.bind(SurfingConfiguration.java:148)

But the path is working while testing on http://jsonpath.com/.

Is it a bug or not supported ?

Thanks

Streaming support

JsonSurf is currently event-driven, let's consider use case of processing big json with JsonPath expression:

  1. it will not load entire json in memory
  2. it will load all results in memory (in a collection) in event listener, in order to return it to caller.

The proposal is to provide an iterator, so that client code can interact with parser in pull manner, getting events one by one.

Parsing maps/jackson

In this document, metricValues is a Map<string,double>.

"metricValues": {
"metric1": 10.977,
"metric2": 0.062111801242236024
}

Variations of this expression:

Collection<Object> metricValues = surfer.collectAll(json, "$.body.metricValues.*");

Are pulling up the numbers (10.977) but not the keys ("metric1").

I've tried to cast to maps, classes with name-value, etc.

Suggestions?

Reusable JsonConfiguration

It is reported by user that the surf(reader, surfingContext) API is not efficient enough because the user have to initiate SurfingContext once and once again for every json processing. Current implementation is too heavy for building SurfingContext from scratch. A reusable configuration object should be introduced to eliminate such overhead.

Gson parser should support lenient parsing

By default Gson's JsonReader class uses strict parsing which seems logical, however once you start trying parse real world json data you frequently run into the following:

MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line ...

It would be nice if the JsonReader used by GsonParser was lenient by default, or at least had a way to configure it for lenient parsing.

Add support for Java 8 Stream

Thanks for your great work. Is it possible to add in support for Java 8 Stream? e.g. return a stream of objects so that the stream can emit new elements as soon as they arrive from the Json input stream.

Documentation

1.API:
collectOne
collectAll
2.ErrorHandlingStrategy
3. performance tuning tips

  1. definite path vs indefinite
  2. skip overlapped path

Collector interface enhancement

Currently, the "collect" interfaces are not handy, because It surfs the whole json and apply only one JsonPath for each collection which is obviously not efficient at all. So I plan to add following interface:

    JsonSurfer surfer = JsonSurferJackson.INSTANCE;
    Builder builder = surfer.configBuilder();
    Holder holder1 = builder.collectOne("$.store");
    Holder holder2 = builder.collectAll("$.store.book[*]");
    builder.buildAndSurfer(json);
    holder1.get();
    holder2.get();

The enhanced interface will allow user to add any desired JsonPath during config building phase and collect everything matched into holders within a single surfing.

Wildward does not work on Pojo

When using jsonpath on objects having arrays or list, wildcard exception does not work. The following exception is thrown :
java.lang.UnsupportedOperationException: Not supported
at org.jsfr.json.path.PathOperator.resolve(PathOperator.java:48)
at org.jsfr.json.path.JsonPath.resolve(JsonPath.java:177)

package test;

import java.util.Arrays;
import java.util.List;

import org.jsfr.json.compiler.JsonPathCompiler;
import org.jsfr.json.path.JsonPath;
import org.jsfr.json.resolver.PoJoResolver;
import org.junit.jupiter.api.Test;

public class JsonSurferTest {

    class B {
        private boolean c = true;

    }

    class A {
        private List<B> b = Arrays.asList(new B[] { new B(), new B()});
        private B[] b2 = new B[] { new B(), new B()};

    }
    
    @Test
    public void testList() {
        A a = new A();
        JsonPath compiledPath = JsonPathCompiler.compile("$.b[*].c");
        compiledPath.resolve(a, new PoJoResolver());
        
    }
    
    @Test
    public void testArray() {
        A a = new A();
        JsonPath compiledPath = JsonPathCompiler.compile("$.b2[*].c");
        compiledPath.resolve(a, new PoJoResolver());
        
    }
}

Not working if node key is a number

It looks like the path could not be parsed if the node key is a number, such as "$.tree.taxonomy.100177". Is there a dedicated expression that used for this case to work?

Expose location from parsing context

I have a use case, where I need to capture the current offset (i.e., line number, column number) in the stream from the ParsingContext. In jackson speak, this is the com.fasterxml.jackson.core.JsonLocation. I could probably capture this manually, by providing a custom JsonFactory that captures the returned parser from the createParser method (I haven't tried) so that I can query the native method.

It would be nice if this is natively supported in the ParsingContext.

Negation in JSON filter

Following JSON path fails- $..book[?(!(@.price<10 && @.category=='fiction'))] while it works fine on http://jsonpath.com/.

Looking at the code seems like the JSON Path grammar (JsonPath.g4) doesn't have negation support. Are there plans for supporting it?

Parsing JSON object

I'm having the following JSON object:

"features": [

{
  "type": "Feature",
  "properties": {
    "@id": "node/643464022",
    "historic": "memorial",
    "image": "imagesource",
    "memorial:addr": "address",
    "memorial:text": "text",
    "memorial:type": "type",
    "memorial:website": "website",
    "name": "First Lastname",
    "network": "network",
    "person:date_of_birth": "date_of_birth",
    "pos": "A1",
    "website": "website",
    "@timestamp": "2015-06-15T20:39:33Z",
    "@version": 10,
    "@changeset": 31992980,
    "@user": "tomtom100",
    "@uid": 2616776
  },
  "geometry": {
    "type": "Point",
    "coordinates": [
      6.9601819,
      50.9455431
    ]
  },
  "id": "node/643464022"
},

Now I wanne look in the whole JSON array after a specifc coordinates, by using jsonSurfer. But when I'm using the code snippet I'm getting a error.

JsonSurfer jsonSurfer = JsonSurferGson.INSTANCE;
        jsonSurfer.configBuilder()
                .bind("$.features[0].geometry[?(@.coordinates=='6.9601819,50.9455431')]", new JsonPathListener() {
                    @Override
                    public void onValue(Object value, ParsingContext context) {
                         System.out.println("Surfing: " +value);
                    }
                })
                .buildAndSurf(itemsJson);

        return null;

The error message is the following:

E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
Process: com.example.android.stolpersteinear, PID: 14459
java.lang.RuntimeException: An error occurred while executing
doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:318)
at
java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:354)
at java.util.concurrent.FutureTask.setException(FutureTask.java:223)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
Caused by: org.antlr.v4.runtime.misc.ParseCancellationException
at
org.antlr.v4.runtime.BailErrorStrategy.recoverInline(BailErrorStrategy.java:66)
at org.antlr.v4.runtime.Parser.match(Parser.java:206)
at
org.jsfr.json.compiler.JsonPathParser.exprEqualStr(JsonPathParser.java:1070)
at org.jsfr.json.compiler.JsonPathParser.expr(JsonPathParser.java:804)
at
org.jsfr.json.compiler.JsonPathParser.filter(JsonPathParser.java:726)
at org.jsfr.json.compiler.JsonPathParser.path(JsonPathParser.java:262)
at
org.jsfr.json.compiler.JsonPathCompiler.compile(JsonPathCompiler.java:216)
at
org.jsfr.json.SurfingConfiguration$Builder.bind(SurfingConfiguration.java:131)
at utils.JSONData.fetchJsonArray(JSONData.java:44)
at utils.JSONLoader.loadInBackground(JSONLoader.java:34)
at utils.JSONLoader.loadInBackground(JSONLoader.java:18)
at
android.content.AsyncTaskLoader.onLoadInBackground(AsyncTaskLoader.java:312)
at
android.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:69)
at
android.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:66)
at android.os.AsyncTask$2.call(AsyncTask.java:304)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133) 
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) 
at java.lang.Thread.run(Thread.java:761) 
Caused by: org.antlr.v4.runtime.InputMismatchException
at
org.antlr.v4.runtime.BailErrorStrategy.recoverInline(BailErrorStrategy.java:61)
at org.antlr.v4.runtime.Parser.match(Parser.java:206) 
at
org.jsfr.json.compiler.JsonPathParser.exprEqualStr(JsonPathParser.java:1070) 
at
org.jsfr.json.compiler.JsonPathParser.expr(JsonPathParser.java:804) 
at
org.jsfr.json.compiler.JsonPathParser.filter(JsonPathParser.java:726) 
at
org.jsfr.json.compiler.JsonPathParser.path(JsonPathParser.java:262) 
at
org.jsfr.json.compiler.JsonPathCompiler.compile(JsonPathCompiler.java:216) 
at
org.jsfr.json.SurfingConfiguration$Builder.bind(SurfingConfiguration.java:131) 
at utils.JSONData.fetchJsonArray(JSONData.java:44) 
at utils.JSONLoader.loadInBackground(JSONLoader.java:34) 
at utils.JSONLoader.loadInBackground(JSONLoader.java:18) 
at
android.content.AsyncTaskLoader.onLoadInBackground(AsyncTaskLoader.java:312) 
at
android.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:69) 
at
android.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:66) 
at android.os.AsyncTask$2.call(AsyncTask.java:304) 
at java.util.concurrent.FutureTask.run(FutureTask.java:237) 
at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133) 
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) 
at java.lang.Thread.run(Thread.java:761)

 

Something like that works perfectly:

JsonSurfer jsonSurfer = JsonSurferGson.INSTANCE;
        jsonSurfer.configBuilder()
                .bind("$.features[0].geometry.coordinates]", new JsonPathListener() {
                    @Override
                    public void onValue(Object value, ParsingContext context) {
                         System.out.println("Surfing: " +value);
                    }
                })
                .buildAndSurf(itemsJson);

        return null;

Thanks,
Bjoern

relative path after filter

I am trying JSON path $.store.book[?(@.price < 10 || @.category =='fiction')].author but getting an error Caused by: org.antlr.v4.runtime.InputMismatchException

is this due to the assumption that after a filter, there may not be a relative path? the antlr grammar file JsonPath.g4 currently has- path: '$' relativePath* filter? EOF;

License

Looks cool. Can you add a license like MIT or whatever you want?

Issue on jsonPath filter

Hello,

Given this json :

{
    "store": {
        "book": [
            {
                "category": "reference",
                "author": "Nigel Rees",
                "title": "Sayings of the Century",
                "price": 8.95,
                "description":{
                       "year":"2000"
                 }
            },
            {
                "category": "fiction",
                "author": "Evelyn Waugh",
                "title": "Sword of Honour",
                "price": 12.99,
                "description":{
                       "year":"2010"
                 }
            },
            {
                "category": "fiction",
                "author": "Herman Melville",
                "title": "Moby Dick",
                "isbn": "0-553-21311-3",
                "price": 8.99,
                "description":{
                       "year":"2000"
                 }
            },
            {
                "category": "fiction",
                "author": "J. R. R. Tolkien",
                "title": "The Lord of the Rings",
                "isbn": "0-395-19395-8",
                "price": 22.99
            }
        ],
        "bicycle": {
            "color": "red",
            "price": 19.95
        }
    },
    "expensive": 10
}

The path $.store.book[?(@.description.year=='2010')]

should return :

{
    "category": "fiction",
    "author": "Evelyn Waugh",
    "title": "Sword of Honour",
    "price": 12.99,
    "description":{
            "year":"2010"
    }
}

But when I execute this code :

JsonSurfer surfer = JsonSurferJackson.INSTANCE;
Object actualValue = surfer.collectOne(json, "$.store.book[?(@.description.year=='2010')]");

I get this exception :

org.antlr.v4.runtime.misc.ParseCancellationException
	at org.antlr.v4.runtime.BailErrorStrategy.recoverInline(BailErrorStrategy.java:66)
	at org.antlr.v4.runtime.Parser.match(Parser.java:206)
	at org.jsfr.json.compiler.JsonPathParser.filter(JsonPathParser.java:726)
	at org.jsfr.json.compiler.JsonPathParser.path(JsonPathParser.java:260)
	at org.jsfr.json.compiler.JsonPathCompiler.compile(JsonPathCompiler.java:220)
	at org.jsfr.json.SurfingConfiguration$Builder.bind(SurfingConfiguration.java:131)
	at org.jsfr.json.JsonSurferTest.testJsonPathFilterDeep(JsonSurferTest.java:117)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.antlr.v4.runtime.InputMismatchException
	at org.antlr.v4.runtime.BailErrorStrategy.recoverInline(BailErrorStrategy.java:61)
	... 29 more

Android compatibility issue

JsonSurfer works in my Note 4 running Android 6 but in my Nexus 4 device which is
running on Android 4.2 it crashes on the second line:

JsonSurfer surfer = JsonSurferGson.INSTANCE;

Object statementIDObj = surfer.collectOne(myResponse, "$.results[0].statement_id");

producing the error: java.lang.NoClassDefFoundError: java.nio.charset.StandardCharsets .

Now I know that Standard Charsets were introduced in KitKat but I wanted to ask if there
is a workaround for this. Thank you

Support script expressions

The current JsonPath spec allows script expressions aka functions, are there any plans to support it?

$.books.size()

$.somestring.split(".")[0]

Supply an OutputStream to a SurfingConfiguration

First: Good job with JsonSurfer! It's a helpful tool, for sure.

I am looking for a way to read from a large JSON file in a streaming fashion and write an excerpt from that file to a new file in a streaming fashion. The onValue method of the JsonPathListener receives an Object which was obtained in a streaming manner. This is great, but at that point the full Object is loaded in memory. Would it be possible to specify an OutputStream to a SurfingConfiguration, such that the matching portions of the incoming file could be written to the stream instead of memory?

Actually, I really need a different OutputStream for every excerpt that is matched by the specified JSON Path. Perhaps a new SurfingConfiguration.setStreamHandler method would receive a new OutputStreamHandler interface that two methods: OutputStream onNewOutput(ParsingContext context) and void end(OutputStream out)... or something. There are a few ways to do it, I'm sure.

Maybe there is already support for something like this. I looked but couldn't find any.

Keys containin a space cannot be parsed

I have a json with some keys that contain spaces ("Image Player". "Phone Number"). There's a ParseCancellationException if I try to parse it:

MyApp.getJsonSurfer().collectOne(jsonAsString, "$.fields.Phone Number")

How to handle when keys have spaces?

Here's the sample json:

{ "id": "some_id", "fields": { "Email": "[email protected]", "Status": "Amateur", "Image player": [ { "id": "some_other_id", "url": "https://www.google.es", "filename": "image.png", "size": 413642, "type": "image/png", "thumbnails": { "small": { "width": 36, "height": 36 }, "large": { "width": 500, "height": 500 }, "full": { "width": 500, "height": 500 } } } ], "Phone Number": "666666666", "Name": "Player one", "Wins": [ 0 ], "Standing": [ "Eliminated" ] }, "createdTime": "2018-06-28T22:32:47.000Z" }

JsonSurfer cannot parse boolean attributes

Given the following JSON

[{"a": true, "b": 1}, {"a": false, "b": 2}, {"a": false, "b": 3}]

and code

JsonSurferJackson.INSTANCE.collectAll(inputStream, "$[?(@.a==false)]")

JsonSurfer fails with

line 1:9 no viable alternative at input '@.a==false'

org.antlr.v4.runtime.misc.ParseCancellationException
    at org.antlr.v4.runtime.BailErrorStrategy.recover(BailErrorStrategy.java:51)
    ...
Caused by: org.antlr.v4.runtime.NoViableAltException

Add Support For Knowing Parsing Completed

Right now on trying to convert this Api to Reactive Streams we don't have any way to
know that parsing completed, hence can con signal onComplete event in Reactive Streams. So please add support for same.
If I am missing something then correct me.

Regression in 1.5.0

Hello,

In 1.4.3, I have a code working well like that :
surfer.configBuilder()
.bind("$.header", (JsonPathListener) (final Object value, final ParsingContext context) -> {
}).bind("$.data[?(@.device-type=='vmanage')]",
(JsonPathListener) (final Object value, final ParsingContext context) -> {
})
.bind("$.data[?(@.device-type=='vsmart')]",
(JsonPathListener) (final Object value, final ParsingContext context) -> {
})
.buildAndSurf(entity.getContent());
Now, in 1.5.0, only the first bind found data. No data for the second and third bind (we have data with version 1.4.3)
If you removed the third bind, the second bind has data.
From my point of view, you have an issue when you have several binding with a filter on same attribute.

Empty results for root array

JsonSurfer returns empty results for the following test, whereas Jayway implementation returns 2 maps correctly:

JsonSurferJackson.INSTANCE.collectAll(
	"[\n" +
	"    {\n" +
	"      \"type\"  : \"iPhone\",\n" +
	"      \"number\": \"0123-4567-8888\"\n" +
	"    },\n" +
	"    {\n" +
	"      \"type\"  : \"home\",\n" +
	"      \"number\": \"0123-4567-8910\"\n" +
	"    }\n" +
	"  ]", JsonPathCompiler.compile("$.*"))
	

GsonParser converting longs to doubles

The GsonParser is converting longs to doubles within the numberHolder implementation. Instead of calling return jsonProvider.primitive(jsonReader.nextString()); perhaps something like this would work better:

final String value = jsonReader.nextString();
try {
    return jsonProvider.primitive(Long.parseLong(value));
}
catch (final NumberFormatException e) {
    return jsonProvider.primitive(Double.parseDouble(value));
}

SurfingContext class should be public

The SurfingContext class should be made public, otherwise its not possible to implement a custom JsonParserAdapter in any package other than org.jsfr.json

Property binding not working when it's coupled with a filtered binding

Hi,

I found a strange behaviour, not sure if it's a bug or it's just me using the API in the wrong manner.
(At least) With JsonSurfer and FastJsonParser, if I try this code

public void testStrangeBehavihour() throws Exception {
        surfer.configBuilder()
                .bind("$.store.book[?(@.category == 'reference')]", new JsonPathListener() {
                    @Override
                    public void onValue(Object value, ParsingContext context) {
                        System.out.println(value);
                    }
                })
                .bind("$.store.bicycle.color", new JsonPathListener() {
                    @Override
                    public void onValue(Object value, ParsingContext context) {
                        System.out.println(value); //This never happen
                    }
                })
                .bind("$.store.bicycle", new JsonPathListener() {
                    @Override
                    public void onValue(Object value, ParsingContext context) {
                        System.out.println(value);
                    }
                }).buildAndSurf(read("sample.json"));
    }

the second listener will never be called.

If I remove the first binding everything works well:

    public void testStrangBehavihour() throws Exception {
        surfer.configBuilder()
                .bind("$.store.bicycle.color", new JsonPathListener() {
                    @Override
                    public void onValue(Object value, ParsingContext context) {
                        System.out.println(value);
                    }
                })
                .bind("$.store.bicycle", new JsonPathListener() {
                    @Override
                    public void onValue(Object value, ParsingContext context) {
                        System.out.println(value);
                    }
                }).buildAndSurf(read("sample.json"));
    }

I tried to find an explanation in the code but found nothing.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.