koush / ion

Android Asynchronous Networking and Image Loading
Other
6.3k stars 1.04k forks source link

[Add Feature] Add Jackson JSON parser #40

Open vipulpurohit opened 11 years ago

vipulpurohit commented 11 years ago

Can you please add Jackson JSON parser in ION like you did for GSON, its because Jackson is much faster then GSON.

Thanks

koush commented 11 years ago

That would make the API fairly wonky... two versions of every JSON call.

meoyawn commented 11 years ago

how about using a Converter object just like Retrofit does? this will remove a dependency on Gson

koush commented 11 years ago

Hm, doing this would be a breaking API change. Let me see how I can go about this gracefully.

koush commented 10 years ago

Check out hte following, that allows you to use a custom parser:

public <T> ResponseFuture<T> as(AsyncParser<T> parser);
deradam commented 10 years ago

Did anyone actually implement a Jackson AsyncParser for Ion?

koush commented 10 years ago

I can provide a sample in the README or something.

deradam commented 10 years ago

That would be nice. We use Jackson in our tool and the conversion is a bit lame (from Gson to String to Jackson JSON).

deradam commented 10 years ago

Hey @koush, I just had a look at your GsonParser and gave it a try. Actually I do realized that I do not need an object back but an instance of the JsonParser that I can pass to different methods to do the actual stream parsing. So I came up with that JacksonParser class:

public class JacksonParser implements AsyncParser<JsonParser> {

    public JacksonParser() {
    }

    @Override
    public Future<JsonParser> parse(DataEmitter emitter) {
        return new ByteBufferListParser().parse(emitter)
        .then(new TransformFuture<JsonParser, ByteBufferList>() {
            @Override
            protected void transform(ByteBufferList result) throws Exception {
                JsonFactory jsonFactory = new JsonFactory();
                JsonParser jsonParser = jsonFactory
                            .createJsonParser(new InputStreamReader(new ByteBufferListInputStream(result)));
                setComplete(null, jsonParser);
            }
        });
    }

    @Override
    public void write(DataSink sink, JsonParser value, CompletedCallback completed) {
        new StringParser().write(sink, "", completed);
    }
}

Now I get a "loaded" Jackson json parser back and can actually do the transformation to the object. I could also create more specific parsers, that would simply give me the target object back. Maybe in a next iteration ;)

However, I do not really get the purpose of the write method. The implementation above makes no sense, as it does not use the value object at all. Maybe you can provide some more infos on that?

Anyway, thanks a lot for this awesome lib :+1:

deradam commented 10 years ago

And as a follow up, a more generic and GsonParser like approach would be this class:

public class JacksonParser<T extends JsonNode> implements AsyncParser<T> {

    public JacksonParser() {
    }

    @Override
    public Future<T> parse(DataEmitter emitter) {
        return new ByteBufferListParser().parse(emitter)
        .then(new TransformFuture<T, ByteBufferList>() {
            @Override
            protected void transform(ByteBufferList result) throws Exception {
                ObjectMapper mapper = new ObjectMapper();
                T node = (T) mapper.readTree(new InputStreamReader(new ByteBufferListInputStream(result)));
                setComplete(null, node);
            }
        });
    }

    @Override
    public void write(DataSink sink, T value, CompletedCallback completed) {
        ObjectMapper mapper = new ObjectMapper();
        try {
            new StringParser().write(sink, mapper.writeValueAsString(value), completed);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

You can call it with the type that you want (ArrayNode, ObjectNode, etc.)

ObjectNode node = Ion.with(context, "http://www.someurl.com/json/").as(new JacksonParser<ObjectNode>()).get();

I am still not very sure about the write method though :)

koush commented 10 years ago

The second example looks correct, write method included.

deradam commented 10 years ago

@koush ah cool! So the write method is like the "reverse" to the parse method? This makes in the second example. With the first it does not really make sense, as what would be the "reverse" to a stream parser!?

Anyhow, I need the stream parser as I need to save memory on big JSON files. Reading the whole into memory like with the GsonParser or the second example does not work for me.

Feel free to share the code somewhere in your comments ;) Then I guess this issue can be closed again!

koush commented 10 years ago

The problem is that streams are blocking constructs. Which is not so good in a async context. It would mean that Ion would need to fire up a background thread.

How big is your JSON Payload (in bytes) that you need a stream to parse it?

Anything under 500k should be fine on most devices.

koush commented 10 years ago

For the write method, it is probably better to write to a bytearrayoutputstream, and then pass that to the sink.

deradam commented 10 years ago

@koush the JSON data is up to 2 MB. I use stream parsing to directly dump data into the database for later access. But for me it's fine if I just receive the "configured" stream parser back, so the first example does the trick.

I checked the classes and I do not see how I would pass a bytearrayoutputstream to the sink.

TonyHaddad91 commented 8 years ago

@deradam Thanks for jackson parser but how i can use it with any model class i have like what we can do with Gson "new TypeToken"