wanglingsong / JsonSurfer

A streaming JsonPath processor in Java
MIT License
292 stars 55 forks source link

Wildward does not work on Pojo #39

Closed acommuni closed 6 years ago

acommuni commented 6 years ago

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());

    }
}
wanglingsong commented 6 years ago

Sorry! As it is said, Resolver API does not support wildcard.

acommuni commented 6 years ago

I think it would be nice to write something in the readme about this. It limit the usage of JSurfer for Pojo.

Do you plan to support whole features for Pojo or not ? How can I help ? Do you have something in mind on how Resolver API need to be modified ?

wanglingsong commented 6 years ago

I think you'd better try the new feature "binary format". Please follow the example in unit test: https://github.com/jsurfer/JsonSurfer/blob/master/jsurfer-all/src/test/java/org/jsfr/json/JacksonParserTest.java#L108 Just turn Pojo into binary and then surfer on it.

Resolver API can be considered as a helper utility and was not designed for complicated JsonPath. Moreover, It does not process json in "Surfing" style, i.e. streaming. So I don't think it's a good idea to spend too much effort on enhancing Resolver API to support full JsonPath feature.

Thanks so much for the advice. I will update readme and provide detailed example on surfing on POJO soon.

acommuni commented 6 years ago

It works fine with simple classes (simple types, arrays, lists) but I have encountered an issue on Jackson protobuff schema generator with date field : https://github.com/FasterXML/jackson-dataformats-binary/issues/140 . It could miss something in loop detection during object tree visit.

acommuni commented 6 years ago

After making some test, I'm not sure using protobuf is the best way. It means converting the whole object to protobuff format and then converting the result to the final object. For instance the result of tatgeting an OffestDateTime is a double that you need to convert into OffsetDateTime. Regarding performance, it's too bad to convert object twice. It could just a tree walking without any conversion

wanglingsong commented 6 years ago

Well, it depends on your use case. If you just need to deal with POJO, I think https://github.com/json-path/JsonPath would be better choice. JsonSurfer is good at streaming processing which means that you can use JsonPath against raw payload, e.g. String or InputStream without deserializing them into POJO

acommuni commented 6 years ago

After some tries and tricks it works well with JsonPath. Thank you!

I'm not sure it's the place to write code for other frmawork, but I do not like issue where guys says that it works now but do not give the solution :)


package test.test_jsurfer;

import java.time.OffsetDateTime;
import java.util.UUID;

import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.TextNode;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.DocumentContext; 
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.spi.json.JacksonJsonNodeJsonProvider;
import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;

public final class JsonPathTest {

    public static final Configuration JACKSON_JSON_NODE_CONFIGURATION;

    static {
        ;
        ObjectMapper om = new ObjectMapper();
        om = om.enable(SerializationFeature.WRITE_DATES_WITH_ZONE_ID);
        om = om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        om = om.registerModule(new JavaTimeModule());

        JACKSON_JSON_NODE_CONFIGURATION = Configuration.defaultConfiguration().addOptions(Option.SUPPRESS_EXCEPTIONS)

                .mappingProvider(new JacksonMappingProvider(om)).jsonProvider(new JacksonJsonNodeJsonProvider(om));
    }

    @Test
    public void writeObject() {

        DocumentContext context = JsonPath.using(JACKSON_JSON_NODE_CONFIGURATION).parse("{}");
        context.put("$", "data", new A());
        Object id = context.read("$.data.b2[*].d");
        System.out.println("id:" + id);
        id = context.read("$.data.b2[?(@.c==false)]");
        System.out.println("id:" + id);
        id = context.read("$.data.b2[*]");
        System.out.println("id:" + id);
        id = OffsetDateTime.parse(((TextNode) context.read("$.data.date")).asText());
        System.out.println("id:" + id);

        System.out.println("ctx:" + context.json());

    }

}
wanglingsong commented 6 years ago

It's OK