Jackson deserializing with a custom deserializer causes many GC calls and takes a lot longer

To solve the type mismatch problem discussed in this thread , I created custom Deserializers and added them to ObjectMapper . However, performance degrades significantly with this.

With the default deserializer, I get 1-2 garbage collection calls in logcat , while with a custom deserializer there are at least 7-8 GC calls, and therefore the processing time also increases significantly.

My deserializer:

 public class Deserializer<T> { public JsonDeserializer<T> getDeserializer(final Class<T> cls) { return new JsonDeserializer<T> (){ @Override public T deserialize(JsonParser jp, DeserializationContext arg1) throws IOException, JsonProcessingException { JsonNode node = jp.readValueAsTree(); if (node.isObject()) { return new ObjectMapper().convertValue(node, cls); } return null; } }; } } 

And I use this to add to the mapper

 public class DeserializerAttachedMapper<T> { public ObjectMapper getMapperAttachedWith(final Class<T> cls , JsonDeserializer<T> deserializer) { ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule(deserializer.toString(), new Version(1, 0, 0, null, null, null)); module.addDeserializer(cls, deserializer); mapper.registerModule(module); return mapper; } } 

EDIT: Additional data added

My JSON is significant size, but not huge: I pasted here

Now to parse the same JSON if I use this code:

  String response = ConnectionManager.doGet(mAuthType, url, authToken); FLog.d("location object response" + response); // SimpleModule module = new SimpleModule("UserModule", new Version(1, 0, 0, null, null, null)); // JsonDeserializer<User> userDeserializer = new Deserializer<User>().getDeserializer(User.class); // module.addDeserializer(User.class, userDeserializer); ObjectMapper mapper = new ObjectMapper(); // mapper.registerModule(module); JsonNode tree = mapper.readTree(response); Integer code = Integer.parseInt(tree.get("code").asText().trim()); if(Constants.API_RESPONSE_SUCCESS_CODE == code) { ExploreLocationObject locationObject = mapper.convertValue(tree.path("response").get("locationObject"), ExploreLocationObject.class); FLog.d("locationObject" + locationObject); FLog.d("locationObject events" + locationObject.getEvents().size()); return locationObject; } return null; 

Then my logcat is like this

But if I use this code for the same JSON

  String response = ConnectionManager.doGet(mAuthType, url, authToken); FLog.d("location object response" + response); SimpleModule module = new SimpleModule("UserModule", new Version(1, 0, 0, null, null, null)); JsonDeserializer<User> userDeserializer = new Deserializer<User>().getDeserializer(User.class); module.addDeserializer(User.class, userDeserializer); ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(module); JsonNode tree = mapper.readTree(response); Integer code = Integer.parseInt(tree.get("code").asText().trim()); if(Constants.API_RESPONSE_SUCCESS_CODE == code) { ExploreLocationObject locationObject = mapper.convertValue(tree.path("response").get("locationObject"), ExploreLocationObject.class); FLog.d("locationObject" + locationObject); FLog.d("locationObject events" + locationObject.getEvents().size()); return locationObject; } return null; 

Then my logcat is like this

+7
source share
2 answers

How big is the object? The code basically builds a tree model (like a tree of houses), and it takes about 3x-5x as much memory as the original document. Therefore, I assume that your input is a huge JSON document.

You can definitely write a more efficient version using the Streaming API. Something like:

 JsonParser jp = mapper.getJsonFactory().createJsonParser(input); JsonToken t = jp.nextToken(); if (t == JsonToken.START_OBJECT) { return mapper.readValue(jp, classToBindTo); } return null; 

it is also possible to implement this with data binding (like JsonDeserializer ), but it gets a little complicated just because you want to delegate the default deserializer. To do this, you will need to implement the BeanDeserializerModifier and replace the standard deserializer when you call "modifyDeserializer": your own code can save the link to the original deserializer and delegate it instead of using an intermediate tree model.

+2
source

If you're not attached to Jackson, you can also try Genson http://code.google.com/p/genson/ . In your case, there are two main advantages: you will not lose performance, it will be easier to implement it. If the property event does not start with an uppercase letter, annotate it with @JsonProperty ("Event") (the same for other properties starting with an uppercase letter). With the following code, you should do the following:

 Genson genson = new Genson.Builder() .withDeserializerFactory(new EventDeserializerFactory()).create(); YourRootClass[] bean = genson.deserialize(json, YourRootClass[].class); class EventDeserializerFactory implements Factory<Deserializer<Event>> { public Deserializer<Event> create(Type type, Genson genson) { return new EventDeserializer(genson.getBeanDescriptorFactory().provide(Event.class, genson)); } } class EventDeserializer implements Deserializer<Event> { private final Deserializer<Event> standardEventDeserializer; public EventDeserializer(Deserializer<Event> standardEventDeserializer) { this.standardEventDeserializer = standardEventDeserializer; } public Event deserialize(ObjectReader reader, Context ctx) throws TransformationException, IOException { if (ValueType.ARRAY == reader.getValueType()) { reader.beginArray().endArray(); return null; } return standardEventDeserializer.deserialize(reader, ctx); } } 
0
source

All Articles