2012-04-26 20 views
18

का उपयोग कर एक और वर्ग पदानुक्रम में जेसन deserialization अब मैं जैक्सन के साथ काम कर रहा हूँ और मेरे पास इसके बारे में कुछ सवाल हैं।जैक्सन

सबसे पहले। मेरे पास दो सेवाएं हैं, पहले डेटा इकट्ठा करना और सेवा भेजना है और दूसरा यह डेटा प्राप्त करता है और, उदाहरण के लिए, इसे फ़ाइल में लॉग इन करें।

तो, पहले सेवा इस तरह वर्ग पदानुक्रम है:

  +----ConcreteC 
     | 
Base ----+----ConcreteA 
     | 
     +----ConcreteB 

और दूसरा सेवा इस तरह वर्ग पदानुक्रम है:

ConcreteAAdapter extends ConcreteA implements Adapter {} 
ConcreteBAdapter extends ConcreteB implements Adapter {} 
ConcreteCAdapter extends ConcreteC implements Adapter {} 

पहली सेवा ConcreteXAdapter बारे में कुछ नहीं जानता है।

तरह से मैं पहली सर्विस पर डेटा भेज रहा:

Collection<Base> data = new LinkedBlockingQueue<Base>() 
JacksonUtils utils = new JacksonUtils(); 
data.add(new ConcreteA()); 
data.add(new ConcreteB()); 
data.add(new ConcreteC()); 
... 
send(utils.marshall(data)); 
... 

public class JacksonUtils { 

    public byte[] marshall(Collection<Base> data) throws IOException { 
     ByteArrayOutputStream out = new ByteArrayOutputStream() { 
      @Override 
      public byte[] toByteArray() { 
       return buf; 
      } 
     }; 

     getObjectMapper().writeValue(out, data); 
     return out.toByteArray(); 
    } 
    protected ObjectMapper getObjectMapper() { 
     return new ObjectMapper(); 
    } 

    public Object unmarshall(byte[] json) throws IOException { 
     return getObjectMapper().readValue(json, Object.class); 
    } 

    public <T> T unmarshall(InputStream source, TypeReference<T> typeReference) throws IOException { 
     return getObjectMapper().readValue(source, typeReference); 
    } 

    public <T> T unmarshall(byte[] json, TypeReference<T> typeReference) throws IOException { 
     return getObjectMapper().readValue(json, typeReference); 
    } 
} 

तो, मैं ConcreteXAdapter का संग्रह में json desirialize को, ConcreteX (ConcreteA -> ConcreteAAdapter, ConcreteB -> ConcreteBAdapter, ConcreteC -> ConcreteCAdapter) का संग्रह में नहीं करना चाहता। यदि मैंने वर्णन किया है कि मैं प्राप्त करना चाहता हूं:

Collection [ConcreteAAdapter, ConcreteBAdapter, ConcreteCAdapter] 

मैं यह कैसे कर सकता हूं?

objectMapper.registerSubtypes(
      new NamedType(ConcreteAAdapter.class, "ConcreteA"), 
      new NamedType(ConcreteBAdapter.class, "ConcreteB"), 
      new NamedType(ConcreteCAdapter.class, "ConcreteC") 
      ); 

// note, that for lists you need to pass TypeReference explicitly 
objectMapper.writerWithType(new TypeReference<List<Base>>() {}) 
    .writeValueAsString(someList); 


    { 
     "@type" : "ConcreteA", 
     ... 
    } 

अक्रमांकन पर यह हो जाएगा::

उत्तर

15

मैंने इस समस्या को कैसे हल किया। यहाँ एक उदाहरण परियोजना के लिए एक वर्ग रेखाचित्र है: class diagram

तो मैं अक्रमांकन के बाद ConcreteAAdapter रूप ConcreteA प्राप्त करना चाहते हैं।

मेरे समाधान ClassNameIdResolver विस्तार करने के लिए (उप-प्रकार कक्षाओं बिना किसी अतिरिक्त कार्यक्षमता और अतिरिक्त फ़ील्ड कहते हैं) उप-प्रकार वर्ग वस्तुओं में आधार वर्ग वस्तुओं deserialize में कार्यक्षमता जोड़ने के लिए है।यहाँ

protected ObjectMapper getObjectMapperForDeserialization() { 
     ObjectMapper mapper = new ObjectMapper(); 

     StdTypeResolverBuilder typeResolverBuilder = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); 
     typeResolverBuilder = typeResolverBuilder.inclusion(JsonTypeInfo.As.PROPERTY); 
     typeResolverBuilder.init(JsonTypeInfo.Id.CLASS, new ClassNameIdResolver(SimpleType.construct(Base.class), TypeFactory.defaultInstance()) { 
      private HashMap<Class, Class> classes = new HashMap<Class, Class>() { 
       { 
        put(ConcreteA.class, ConcreteAAdapter.class); 
        put(ConcreteB.class, ConcreteBAdapter.class); 
        put(ConcreteC.class, ConcreteCAdapter.class); 
       } 
      }; 

      @Override 
      public String idFromValue(Object value) { 
       return (classes.containsKey(value.getClass())) ? value.getClass().getName() : null; 
      } 

      @Override 
      public JavaType typeFromId(String id) { 
       try { 
        return classes.get(Class.forName(id)) == null ? super.typeFromId(id) : _typeFactory.constructSpecializedType(_baseType, classes.get(Class.forName(id))); 
       } catch (ClassNotFoundException e) { 
        // todo catch the e 
       } 
       return super.typeFromId(id); 
      } 
     }); 
     mapper.setDefaultTyping(typeResolverBuilder); 
     return mapper; 
    } 

और एक कोड है जो क्रमबद्धता के लिए ObjectMapper बनाने है:

यहाँ एक कोड है जो अक्रमांकन के लिए ObjectMapper बनाता है

protected ObjectMapper getObjectMapperForSerialization() { 
    ObjectMapper mapper = new ObjectMapper(); 

    StdTypeResolverBuilder typeResolverBuilder = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); 
    typeResolverBuilder = typeResolverBuilder.inclusion(JsonTypeInfo.As.PROPERTY); 
    typeResolverBuilder.init(JsonTypeInfo.Id.CLASS, new ClassNameIdResolver(SimpleType.construct(Base.class), TypeFactory.defaultInstance())); 
    mapper.setDefaultTyping(typeResolverBuilder); 

    return mapper; 
} 

टेस्ट कोड:

public static void main(String[] args) throws IOException { 
    JacksonUtils JacksonUtils = new JacksonUtilsImpl(); 

    Collection<Base> data = new LinkedBlockingQueue<Base>(); 
    data.add(new ConcreteA()); 
    data.add(new ConcreteB()); 
    data.add(new ConcreteC()); 

    String json = JacksonUtils.marshallIntoString(data); 

    System.out.println(json); 

    Collection<? extends Adapter> adapters = JacksonUtils.unmarshall(json, new TypeReference<ArrayList<Adapter>>() {}); 

    for (Adapter adapter : adapters) { 
     System.out.println(adapter.getClass().getName()); 
    } 
} 

पूर्ण जैक्सन का कोड वर्ग:

public class JacksonUtilsImpl implements JacksonUtils { 

    @Override 
    public byte[] marshall(Collection<Base> data) throws IOException { 
     ByteArrayOutputStream out = new ByteArrayOutputStream() { 
      @Override 
      public byte[] toByteArray() { 
       return buf; 
      } 
     }; 

     getObjectMapperForSerialization().writerWithType(new TypeReference<Collection<Base>>() {}).writeValue(out, data); 
     return out.toByteArray(); 
    } 

    @Override 
    public String marshallIntoString(Collection<Base> data) throws IOException { 
     return getObjectMapperForSerialization().writeValueAsString(data); 
    } 

    protected ObjectMapper getObjectMapperForSerialization() { 
     ObjectMapper mapper = new ObjectMapper(); 

     StdTypeResolverBuilder typeResolverBuilder = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); 
     typeResolverBuilder = typeResolverBuilder.inclusion(JsonTypeInfo.As.PROPERTY); 
     typeResolverBuilder.init(JsonTypeInfo.Id.CLASS, new ClassNameIdResolver(SimpleType.construct(Base.class), TypeFactory.defaultInstance())); 
     mapper.setDefaultTyping(typeResolverBuilder); 

     return mapper; 
    } 

    protected ObjectMapper getObjectMapperForDeserialization() { 
     ObjectMapper mapper = new ObjectMapper(); 

     StdTypeResolverBuilder typeResolverBuilder = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); 
     typeResolverBuilder = typeResolverBuilder.inclusion(JsonTypeInfo.As.PROPERTY); 
     typeResolverBuilder.init(JsonTypeInfo.Id.CLASS, new ClassNameIdResolver(SimpleType.construct(Base.class), TypeFactory.defaultInstance()) { 
      private HashMap<Class, Class> classes = new HashMap<Class, Class>() { 
       { 
        put(ConcreteA.class, ConcreteAAdapter.class); 
        put(ConcreteB.class, ConcreteBAdapter.class); 
        put(ConcreteC.class, ConcreteCAdapter.class); 
       } 
      }; 

      @Override 
      public String idFromValue(Object value) { 
       return (classes.containsKey(value.getClass())) ? value.getClass().getName() : null; 
      } 

      @Override 
      public JavaType typeFromId(String id) { 
       try { 
        return classes.get(Class.forName(id)) == null ? super.typeFromId(id) : _typeFactory.constructSpecializedType(_baseType, classes.get(Class.forName(id))); 
       } catch (ClassNotFoundException e) { 
        // todo catch the e 
       } 
       return super.typeFromId(id); 
      } 
     }); 
     mapper.setDefaultTyping(typeResolverBuilder); 
     return mapper; 
    } 

    @Override 
    public Object unmarshall(byte[] json) throws IOException { 
     return getObjectMapperForDeserialization().readValue(json, Object.class); 
    } 

    @Override 
    public <T> T unmarshall(InputStream source, TypeReference<T> typeReference) throws IOException { 
     return getObjectMapperForDeserialization().readValue(source, typeReference); 
    } 

    @Override 
    public <T> T unmarshall(byte[] json, TypeReference<T> typeReference) throws IOException { 
     return getObjectMapperForDeserialization().readValue(json, typeReference); 
    } 

    @Override 
    public <T> Collection<? extends T> unmarshall(String json, Class<? extends Collection<? extends T>> klass) throws IOException { 
     return getObjectMapperForDeserialization().readValue(json, klass); 
    } 


    @Override 
    public <T> Collection<? extends T> unmarshall(String json, TypeReference typeReference) throws IOException { 
     return getObjectMapperForDeserialization().readValue(json, typeReference); 
    } 
} 
27

इस प्रयोजन के लिए आप JSON में अतिरिक्त जानकारी पारित करने के लिए की जरूरत है:

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, 
     include=JsonTypeInfo.As.PROPERTY, property="@type") 
class Base { 
... 
} 

फिर क्रमबद्धता पर यह @type क्षेत्र जोड़ना होगा

objectMapper.registerSubtypes(
      new NamedType(ConcreteA.class, "ConcreteA"), 
      new NamedType(ConcreteB.class, "ConcreteB"), 
      new NamedType(ConcreteC.class, "ConcreteC") 
      ); 
    objectMapper.readValue(....) 

यहां अधिक: http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

+0

mapper.getDeserializationConfig().addMixInAnnotations(Base.class, BaseMixin.class); mapper.getSerializationConfig().addMixInAnnotations(Base.class, BaseMixin.class); 

उदाहरण BaseMixin वर्ग

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type") @JsonSubTypes({ @JsonSubTypes.Type(value=ConcreteA.class, name="ConcreteA"), @JsonSubTypes.Type(value=ConcreteB.class, name="ConcreteB") }) private static class BaseMixin { } 

दूसरी सेवा पर आप इस तरह BaseMixin निर्धारित कर सकते हैं तुरंत जवाब के लिए धन्यवाद! मैंने सवाल अपडेट किया। संक्षेप में, पहली सेवा 'ConcreteXAdapter' के बारे में कुछ भी नहीं जानता है। तो सवाल यह है कि जैक्सन को 'कंक्रीटएक्स एडाप्टर' बनाने के लिए कैसे कहना है यदि उसे जेसन में 'कंक्रीटएक्स' मिला। – pbespechnyi

+1

इस मामले में मैं मैन्युअल रूप से पास करने का सुझाव दूंगा (जैसे "@type": "ConcreteA") और फिर संपत्ति के आधार पर दूसरी ओर deserialize। अर्थात। इसके लिए आपको कस्टम सीरिएलाइज़र को लागू करने की आवश्यकता है। –

+0

तो बॉक्स से बाहर करने का कोई तरीका नहीं है। ठीक है, मदद के लिए धन्यवाद! – pbespechnyi

9

मुझे प्रोग्रामरब्रस का दृष्टिकोण काम करने के लिए सबसे स्पष्ट और आसान होने के लिए मिलता है (नीचे उदाहरण)। https://stackoverflow.com/a/6339600/1148030 और संबंधित ब्लॉग पोस्ट:: http://programmerbruce.blogspot.fi/2011/05/deserialize-json-with-jackson-into.html

इसके अलावा इस अनुकूल विकि पृष्ठ (भी यूजीन Retunsky के जवाब में बताया गया) की जाँच: http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

मैं एक संबंधित सवाल करने के लिए अपने जवाब से जानकारी मिल गया एक और अच्छा विकि पृष्ठ:

: http://wiki.fasterxml.com/JacksonMixInAnnotations

यहाँ एक छोटी उदाहरण आप जानकारी देना है इस तरह ObjectMapper कॉन्फ़िगर करें: (। एक आंतरिक वर्ग के रूप में परिभाषित करने के लिए आसान):

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type") 
@JsonSubTypes({ 
    @JsonSubTypes.Type(value=ConcreteAAdapter.class, name="ConcreteA"), 
    @JsonSubTypes.Type(value=ConcreteBAdapter.class, name="ConcreteB") 
}) 
private static class BaseMixin { 
} 
+0

आपका समाधान पहले देखो के लिए अधिक स्वच्छ और सही है। अब मैं यह कहने में सक्षम नहीं हूं कि यह मेरे लिए उपयुक्त है, क्योंकि यह बहुत समय पहले था। लेकिन वैसे भी जवाब के लिए धन्यवाद! – pbespechnyi