2014-07-22 13 views
10

मैं निम्नलिखित JSON के साथ संग्रह लागू करने के लिए विफल रहता है।deserializing एक वर्ग जैक्सन

public class ItemList { 
    @JsonProperty("item") 
    List<Item> items; 

    // Getters, setters & co. 
    // ... 
} 

सब कुछ इस बात पर निर्भर अच्छी तरह से काम कर रहा है:

तो मैं deserialize करने के लिए है कि एक वर्ग बनाया है।

अब, मेरे जीवन को कहीं और आसान बनाने के लिए, मैंने फैसला किया कि आइटमलिस्ट ऑब्जेक्ट पर पुन: प्रयास करने में सक्षम होना अच्छा होगा और इसे संग्रह इंटरफ़ेस को लागू करने दें।

तो मूल रूप से मेरी कक्षा में बदल गया:

public class ItemList implements Collection<Item>, Iterable<Item> { 
    @JsonProperty("item") 
    List<Item> items; 

    // Getters, setters & co. 

    // Generated all method delegates to items. For instance: 
    public Item get(int position) { 
    return items.get(position); 
    } 
} 

कार्यान्वयन ठीक से और अच्छी तरह से काम करता है। हालांकि, deserialization अब विफल रहता है।

ऐसा लगता है कि जैक्सन भ्रमित हो रही है:

com.fasterxml.jackson.databind.JsonMappingException: START_OBJECT टोकन से बाहर com.example.ItemList के कहने deserialize नहीं सकते हैं

मेरे पास है @JsonDeserialize(as=ItemList.class) जोड़ने की कोशिश की लेकिन यह चाल नहीं हुई।

जाने का क्या तरीका है?

+0

मैं बहुत यकीन है कि http://stackoverflow.com/users/22656/jon-skeet इस जवाब देने में सक्षम हो जाएगा हूँ:

यहाँ कोड है। इसके अतिरिक्त, आप http://stackoverflow.com/a/21279016/1382251 में कुछ उपयोगी जानकारी पा सकते हैं। –

+1

हालांकि यह एक अच्छा सवाल है, यदि आप चाहते हैं कि पुन: प्रयास करना है, तो अपनी कक्षा को केवल पुन: प्रयोज्य बनाने के बजाय संग्रह क्यों बनाएं? – chrylis

+0

@chrylis: ताकि मैं (आइटम टी: आइटमलिस्ट) ' –

उत्तर

4

स्पष्ट रूप से यह काम नहीं करता है क्योंकि जैक्सन जावा संग्रह प्रकारों के लिए मानक संग्रह deserialiser का उपयोग करता है जो ItemList गुणों के बारे में कुछ भी नहीं जानता है।

यह काम करना संभव है लेकिन बहुत ही सुरुचिपूर्ण तरीके से नहीं। संबंधित संग्रह के लिए मैन्युअल रूप से बनाए गए बीन deserialiser पर डिफ़ॉल्ट संग्रह deserialiser को प्रतिस्थापित करने के लिए आपको ObjectMapper कॉन्फ़िगर करने की आवश्यकता है। मैंने एक उदाहरण लिखा है जो कस्टम एनोटेशन के साथ एनोटेटेड सभी कक्षाओं के लिए BeanDeserializerModifier में करता है।

नोट मैं ObjectMapper ओवरराइड करने के लिए है कि के बाद से सेम संशोधक यह करने के लिए पहुँच नहीं है एक उचित deserialisation संदर्भ बनाने के लिए एक सुरक्षित विधि ObjectMapper की createDeserializationContext के लिए उपयोग मिलता है।

public class JacksonCustomList { 
    public static final String JSON = "{\n" + 
      " \"item\": [\n" + 
      " { \"foo\": 1 },\n" + 
      " { \"foo\": 2 }\n" + 
      " ]\n" + 
      "} "; 

    @Retention(RetentionPolicy.RUNTIME) 
    public static @interface PreferBeanDeserializer { 

    } 

    public static class Item { 
     public int foo; 

     @Override 
     public String toString() { 
      return String.valueOf(foo); 
     } 
    } 

    @PreferBeanDeserializer 
    public static class ItemList extends ArrayList<Item> { 
     @JsonProperty("item") 
     public List<Item> items; 

     @Override 
     public String toString() { 
      return items.toString(); 
     } 
    } 

    public static class Modifier extends BeanDeserializerModifier { 
     private final MyObjectMapper mapper; 

     public Modifier(final MyObjectMapper mapper) { 
      this.mapper = mapper; 
     } 

     @Override 
     public JsonDeserializer<?> modifyCollectionDeserializer(
       final DeserializationConfig config, 
       final CollectionType type, 
       final BeanDescription beanDesc, 
       final JsonDeserializer<?> deserializer) { 
      if (type.getRawClass().getAnnotation(PreferBeanDeserializer.class) != null) { 
       DeserializationContext context = mapper.createContext(config); 
       try { 
        return context.getFactory().createBeanDeserializer(context, type, beanDesc); 
       } catch (JsonMappingException e) { 
        throw new IllegalStateException(e); 
       } 

      } 
      return super.modifyCollectionDeserializer(config, type, beanDesc, deserializer); 
     } 
    } 

    public static class MyObjectMapper extends ObjectMapper { 
     public DeserializationContext createContext(final DeserializationConfig cfg) { 
      return super.createDeserializationContext(getDeserializationContext().getParser(), cfg); 
     } 
    } 

    public static void main(String[] args) throws IOException { 
     final MyObjectMapper mapper = new MyObjectMapper(); 
     SimpleModule module = new SimpleModule(); 
     module.setDeserializerModifier(new Modifier(mapper)); 

     mapper.registerModule(module); 
     System.out.println(mapper.readValue(JSON, ItemList.class)); 
    } 

} 
+1

+1 अच्छा। मैंने 'BeanDeserializerModifier.modifyCollectionDeserializer' का उपयोग करके एक समान समाधान के साथ आने का प्रयास करने में कुछ घंटे बिताए हैं, लेकिन यह पता नहीं लगा सकता कि 'DeserializationContext' कैसे प्राप्त करें। –

+0

इसे संभालने के लिए एक शानदार तरीका दिखता है, मैं इसे आज़मा दूंगा। –

+1

एक सुझाव: उप-वर्गीकरण 'ऑब्जेक्टमैपर' के बजाय, आपको 'कलेक्स्टुअल डीसेरियलाइज़र' को कस्टम संग्रह deserializer के साथ संयोजित करने में सक्षम होना चाहिए: इससे आपको मूल्य deserializer को खोजने/संशोधित करने की सुविधा मिलती है, और गारंटी भी दी जाती है कि इसे ठीक से शुरू किया जाएगा। कुछ विशेष मामलों (पॉलिमॉर्फिक प्रकारों के लिए समर्थन) को संभालने के लिए डिफ़ॉल्ट संग्रह धारावाहिकों पर एक नज़र डालने का अर्थ हो सकता है। – StaxMan

2

यदि आप item संपत्ति जड़ मूल्य पर विचार, आप की तुलना में अपने ItemList वर्ग इस प्रकार बदल सकते हैं, का उपयोग कर @JsonRootNameannotation:

@JsonRootName("item") 
public class ItemList implements Collection<Item>, Iterable<Item> { 
    private List<Item> items = new ArrayList<>(); 

    public Item get(int position) { 
     return items.get(position); 
    } 

    // implemented methods deferring to delegate 
    // ... 
} 

आप तो UNWRAP_ROOT_VALUEdeserialization feature, चीजों को अपेक्षा के अनुरूप काम को सक्रिय करते हैं :

String json = "{\"item\": [{\"foo\": 1}, {\"foo\": 2}]}"; 
ObjectMapper mapper = new ObjectMapper(); 
ObjectReader reader = mapper.reader(ItemList.class); 

ItemList itemList = reader 
     .with(DeserializationFeature.UNWRAP_ROOT_VALUE) 
     .readValue(json); 

क्रमबद्धता समान रूप से अच्छी तरह से काम करता है, WRAP_ROOT_VALUEserialization feature सक्षम के साथ:

ObjectMapper mapper = new ObjectMapper(); 
ObjectWriter writer = mapper.writer(); 

Item item1 = new Item(); 
item1.setFoo(1); 

Item item2 = new Item(); 
item2.setFoo(2); 

ItemList itemList = new ItemList(); 
itemList.add(item1); 
itemList.add(item2); 

String json = writer 
     .with(SerializationFeature.WRAP_ROOT_VALUE) 
     .writeValueAsString(itemList); 

// json contains {"item":[{"foo":1},{"foo":2}]} 

यह समाधान स्पष्ट रूप से पर्याप्त नहीं है, तो अपने ItemList अतिरिक्त गुणों (वास्तविक सूची के अलावा अन्य) भी है कि धारावाहिक जा करने के लिए/deserialized की आवश्यकता होगी शामिल होगा।

+0

अच्छा कामकाज, लेकिन मैं भविष्य के लिए अतिरिक्त संपत्तियों को संभालने की संभावना रखना चाहता हूं। –

संबंधित मुद्दे