2012-11-01 19 views
7

爲了解決我討論的類型不匹配問題in this thread我創建了自定義Deserializers並將它們添加到ObjectMapper。然而,這種性能會顯着惡化。Jackson使用自定義解串器進行反序列化會導致很多GC調用並花費很多時間

使用默認解串器時,我在logcat中獲得1-2個垃圾收集調用,而使用自定義解串器時,至少有7-8個GC調用,因此處理時間也顯着增加。

我解串器:

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

,我使用這個添加到映射器

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

編輯:增加了額外的數據

我的JSON是相當的規模,但不是很大的: 我已粘貼it here

現在解析相同的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;  

然後我logcat的是like this

但是,如果我用同樣的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;   

然後我logcat的是like this

+0

一個意見:一定要確保重用'O​​bjectMapper' - 這是一個重量級的對象,每次請求不應該被創建一次。否則,它肯定會導致很多GC活動。很難從上面的代碼肯定知道。 – StaxMan

+0

其實我注意到你的反序列化器創建了一個ObjectMapper:這非常昂貴。你可以通過使用JsonParser來避免這種情況。getCodec()',將結果轉換爲ObjectMapper(這是安全的upcast)。這應該也有點幫助。 – StaxMan

+0

我修改了我的代碼並將對象映射器添加到了我的單例中,默認解序器的性能得到了改進,但自定義解串器的問題仍然存在。另外,我注意到如果我將兩個反序列化器添加到同一個模塊,那麼默認的反序列化器會被調用而不是自定義的。 'JsonParser.getCodec()'不是一個靜態方法,所以我將不得不爲每個請求創建'JsonParser'。這不會太貴嗎? – vKashyap

回答

2

對象有多大?代碼基本上構建了一個樹模型(dom樹),並且這將佔用與原始文檔相當的3倍至5倍的內存。所以我假設你的輸入是一個巨大的JSON文檔。

您絕對可以使用Streaming API編寫更高效的版本。喜歡的東西:

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

,也可以用數據綁定(如JsonDeserializer)來實現這一點,但它變得有點複雜,因爲你要委託到「默認」解串器。 爲此,您需要實現BeanDeserializerModifier,並在調用「modifyDeserializer」時替換標準解串器:您自己的代碼可以保留對原始解串器的引用並委託給它,而不是使用中間樹模型。

+0

我編輯了我的問題並添加了JSON,解析代碼和日誌。謝謝 – vKashyap

0

如果你不綁在傑克遜,你也可以嘗試Genson http://code.google.com/p/genson/。 在你的情況下,有兩個主要優點:你不會失去性能,應該更容易實現。如果屬性事件沒有以大寫字母開頭,則使用@JsonProperty(「Event」)對它進行註釋(對於以大寫字母開頭的其他屬性,則相同)。 用下面的代碼,你應該做到:附加

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

感謝您的回答。一定會給它一個鏡頭。但是,atm我會先嚐試用jackson提高性能。 – vKashyap

相關問題