2012-04-26 36 views
18

Bây giờ tôi đang làm việc với Jackson và tôi có một số câu hỏi về nó.Json deserialization vào một hệ thống phân cấp lớp khác bằng cách sử dụng Jackson

Trước hết. Tôi có hai dịch vụ, đầu tiên là thu thập dữ liệu và gửi dịch vụ và thứ hai nhận dữ liệu này và, ví dụ, đăng nhập vào một tệp.

Vì vậy, dịch vụ đầu tiên có hệ thống phân cấp lớp như thế này:

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

Và thứ hai dịch vụ có hệ thống phân cấp lớp như thế này:

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

Các dịch vụ đầu tiên không biết gì về ConcreteXAdapter.

Cách tôi đang gửi dữ liệu trên dịch vụ đầu tiên:

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

Vì vậy, tôi muốn desirialize json vào Bộ sưu tập của ConcreteXAdapter, không vào Bộ sưu tập của ConcreteX (ConcreteA -> ConcreteAAdapter, ConcreteB -> ConcreteBAdapter, ConcreteC -> ConcreteCAdapter). Trong trường hợp tôi mô tả tôi muốn nhận được:

Collection [ConcreteAAdapter, ConcreteBAdapter, ConcreteCAdapter] 

Làm cách nào tôi có thể thực hiện việc này?

Trả lời

15

Cách tôi giải quyết vấn đề này. Dưới đây là sơ đồ lớp cho một dự án ví dụ: class diagram

Vì vậy, tôi muốn lấy mẫu ConcreteAAdapterConcreteA sau khi deserialization.

Giải pháp của tôi là mở rộng ClassNameIdResolver để thêm chức năng deserialize các đối tượng lớp cơ sở vào đối tượng lớp con

Dưới đây là một mã mà tạo ra ObjectMapper cho deserialization:

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

Và đây là một mã mà tạo ObjectMapper cho serialization:

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

mã kiểm tra:

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

Full mã của lớp JacksonUtils:

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

Đối với mục đích này, bạn cần phải vượt qua thông tin bổ sung trong JSON:

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

Sau đó, trên serialization nó sẽ thêm @type lĩnh vực:

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", 
     ... 
    } 

trên deserialization nó sẽ là:

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

Thêm tại đây: http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

+0

Cảm ơn bạn đã trả lời nhanh chóng! Tôi đã cập nhật câu hỏi. Tóm lại, dịch vụ đầu tiên không biết gì về 'ConcreteXAdapter'. Vì vậy, câu hỏi là làm thế nào để nói với Jackson để tạo ra 'ConcreteXAdapter' nếu nó tìm thấy' ConcreteX' trong json. – pbespechnyi

+1

Trong trường hợp này, tôi khuyên bạn nên chuyển kiểu bằng tay (như "@type": "ConcreteA") và sau đó deserialize ở phía bên kia dựa trên thuộc tính. I E. bạn cần phải thực hiện một serializer tùy chỉnh cho việc này. –

+0

Vì vậy, không có cách nào để làm điều này ra khỏi hộp. Ok, cảm ơn vì sự giúp đỡ! – pbespechnyi

9

Tôi thấy cách tiếp cận của lập trình viên là rõ ràng nhất và dễ làm việc (ví dụ bên dưới). Tôi đã nhận các thông tin từ câu trả lời của mình cho một câu hỏi liên quan: https://stackoverflow.com/a/6339600/1148030 và bài viết trên blog có liên quan: http://programmerbruce.blogspot.fi/2011/05/deserialize-json-with-jackson-into.html

Ngoài ra kiểm tra trang wiki này thân thiện (cũng đề cập đến trong câu trả lời Eugene Retunsky của): http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

một trang wiki đẹp: http://wiki.fasterxml.com/JacksonMixInAnnotations

Dưới đây là một ví dụ ngắn để cung cấp cho bạn những ý tưởng:

Cấu hình ObjectMapper như thế này: (. Dễ dàng để xác định như một lớp bên trong)

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

Ví dụ BaseMixin lớp

@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 { 
} 

Mở dịch vụ thứ hai bạn có thể xác định BaseMixin như thế này:

@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

Giải pháp của bạn sạch sẽ và chính xác hơn cho giao diện đầu tiên. Bây giờ tôi không thể nói là nó phù hợp với tôi, vì đã lâu lắm rồi. Nhưng dù sao cảm ơn bạn đã trả lời! – pbespechnyi

Các vấn đề liên quan