2012-11-01 8 views
7

Aby rozwiązać problem z niedopasowaniem typu, omówiono in this thread Utworzono niestandardowe Deserializers i dodano je do ObjectMapper. Jednak wydajność znacznie się pogarsza.Proces deserializacji w systemie Jackson z niestandardowym deserializerem powoduje wiele wywołań GC i zajmuje dużo dłużej.

Przy domyślnym deserializatorze otrzymuję 1-2 wywołania funkcji usuwania śmieci w logcat, natomiast w przypadku niestandardowego deserializera są wywoływane co najmniej 7-8 wywołań GC, a zatem czas przetwarzania również znacznie się zwiększa.

My Deserializator:

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; 
    } 
}; 
} 
} 

i używam tego, aby dodać do 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: Dodano dodatkowe informacje

Moje JSON jest o znacznych rozmiarach, ale nie ogromny : Wkleiłem it here

Teraz do analizowania tego samego JSON, jeśli mogę użyć tego kodu:

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;  

Wtedy mój logcat jest like this

Ale jeśli mogę użyć tego kodu na samym 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;   

Wtedy mój logcat jest like this

+0

Jeden dodatkowy komentarz: zrobić absolutnie pewien, że ponowne użycie 'ObjectMapper' - jest to obiekt o dużej gramaturze i nie powinny być tworzone raz na zamówienie. W przeciwnym razie może zdecydowanie spowodować dużą aktywność GC. Trudno o tym wiedzieć z samego kodu powyżej. – StaxMan

+0

Faktycznie zauważyłem, że twój deserializator tworzy obiekt ObjectMapper: jest to bardzo kosztowne. Możesz tego uniknąć, używając 'JsonParser.getCodec() ', rzutując wynik na' ObjectMapper' (co jest bezpiecznym upcastem). To też powinno trochę pomóc. – StaxMan

+0

Zmodyfikowałem swój kod i dodałem obiekt odwzorowujący obiekty do mojego singletonu, wydajność poprawiła się dla domyślnego deserializera, ale problem z niestandardowym deserializerem nadal istnieje. Zauważyłem również, że jeśli dodaję dwa deserializery do tego samego modułu, to domyślny deserializer zostanie wywołany nie jako niestandardowy. 'JsonParser.getCodec()' nie jest metodą statyczną, więc będę musiał utworzyć 'JsonParser' dla każdego żądania. Czy to nie będzie zbyt drogie? – vKashyap

Odpowiedz

2

Jak duży jest obiekt? Kod w zasadzie buduje model drzewa (rodzaj drzewa dom), a to zajmie coś w rodzaju 3x-5x tyle pamięci, ile oryginalny dokument. Zakładam, że twój wkład to ogromny dokument JSON.

Możesz zdecydowanie napisać bardziej wydajną wersję przy użyciu Streaming API. Coś jak:

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

możliwe jest również, aby wdrożyć to z wiązania danych (jak JsonDeserializer), ale robi się nieco skomplikowane, tylko dlatego, że chce przekazać „default” Deserializator. Aby to zrobić, musisz zaimplementować BeanDeserializerModifier i zastąpić standardowy deserializator po wywołaniu "modifyDeserializer": Twój własny kod może zachować odniesienie do pierwotnego deserializera i delegować do niego, zamiast używać pośredniego modelu drzewa.

+0

Edytowałem moje pytanie i dodałem JSON, kod parsujący i logi. Dzięki – vKashyap

0

Jeśli nie jesteś przywiązany do jackson, możesz również wypróbować Genson http://code.google.com/p/genson/. W twoim przypadku istnieją dwie główne zalety: nie stracisz wydajności, powinno być łatwiejsze do wdrożenia. Jeśli zdarzenie właściwości nie rozpoczyna się od górnej litery, należy dodać adnotację do @JsonProperty ("Zdarzenie") (to samo dotyczy innych właściwości zaczynających się od górnej litery). z następującego kodu powinna być sporządzona:

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

Dziękuję za odpowiedź. z pewnością da mu szansę. Ale, atm, najpierw spróbuję poprawić wydajność za pomocą jacksona. – vKashyap

Powiązane problemy