2013-07-01 25 views
34

Đây là một câu hỏi trùng lặp vì các câu hỏi sau hoặc là lộn xộn hoặc họ chưa được giải đáp tại tất cả:Jackson thực sự không thể loại bỏ json thành một loại chung?

deserializing-a-generic-type-with-jackson

jackson-deserialize-into-runtime-specified-class

jackson-deserialize-using-generic-class

jackson-deserialize-generic-class-variable

Tôi hy vọng rằng điều này câu hỏi cuối cùng sẽ tìm thấy một câu trả lời mà làm cho điều này rõ ràng cho tốt.

Có một mô hình:

public class AgentResponse<T> { 

    private T result; 

    public AgentResponse(T result) { 
     this.result = result; 
    } 
    public T getResult() { 
     return result; 
    } 
} 

JSON đầu vào:

{"result":{"first-client-id":3,"test-mail-module":3,"third-client-id":3,"second-client-id":3}} 

và hai đề nghị cách deserializing kiểu generic:

mapper.readValue(out, new TypeReference<AgentResponse<Map<String, Integer>>>() {}); 

hoặc

JavaType javaType = mapper.getTypeFactory().constructParametricType(AgentResponse.class, Map.class); 
mapper.readValue(out, javaType); 

Jackson không bao giờ có thể đối phó với loại T chung, nó tính toán nó là một bản đồ từ JavaType, nhưng nó tìm đối số constructor kiểu đối tượng vì loại xóa và ném một lỗi. Vì vậy, đây là một lỗi Jackson, hoặc tôi đang làm điều gì đó sai? Điều gì khác là đặc điểm kỹ thuật rõ ràng của TypeReference hoặc JavaType cho?

com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class com.fg.mail.smtp.AgentResponse<java.util.Map<java.lang.String,java.lang.Integer>>]: can not instantiate from JSON object (need to add/enable type information?) 
at [Source: [email protected]; line: 1, column: 2] 
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:164) 
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:984) 
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:276) 
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121) 
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2888) 
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2064) 
+0

Bạn nên đăng câu hỏi đó lên danh sách gửi thư của người dùng jackson – fge

+0

Ngoài ra, bạn có thực sự cố gắng và deserialize _all_ rằng JSON hay chỉ là giá trị thành viên 'kết quả' của JSON? – fge

+0

Tôi đang deserializing đầu vào JSON vào AgentResponse. Tôi không biết làm thế nào và tại sao tôi sẽ deserialize nó một phần. Nó chỉ là một phản ứng với trạng thái và giá trị kết quả, có thể là Object, collection, map, vv. Tôi đã sửa đổi câu hỏi để chỉ có kết quả chung, – lisak

Trả lời

49

Bạn cần thêm một số chú thích trên hàm tạo để cho Jackson biết cách tạo đối tượng. Những điều sau đây đã làm việc cho tôi:

public class AgentResponse<T> { 

    private T result; 

    @JsonCreator 
    public AgentResponse(@JsonProperty("result") T result) { 
     this.result = result; 
    } 
    public T getResult() { 
     return result; 
    } 
} 

Chú thích không thể gọi là nhà xây dựng này. Và không có chú thích @JsonProperty, Jackson không biết rằng đối số đầu tiên của hàm tạo bản đồ cho thuộc tính result.

+3

Man bạn đã lưu ngày của tôi, tôi đã gỡ lỗi jackson stack tất cả các con đường xuống và tôi thấy nó tìm thấy các đối số của hàm tạo, vì vậy tôi nghĩ đó là vấn đề chung. Trong thực tế, vấn đề thực sự là jackson không thể biết tên đối số của hàm tạo nên JVM không cung cấp nó. Chết tiệt tôi nên thử mà không có generics đầu tiên ... Cảm ơn! – lisak

+4

Và nếu bạn không thể hoặc không muốn chú thích trực tiếp lớp học, bạn có thể chú thích một mixin thay thế: http://wiki.fasterxml.com/JacksonMixInAnnotations – dnault

4

Tôi đã thử sử dụng cùng một cách tiếp cận nhưng tôi chưa chú thích lớp mô hình của mình. Nó làm việc tốt cho tôi.

Đây là mô hình của tôi lớp

public class BasicMessage<T extends Serializable> implements Message<T> { 
    private MessageHeader messageHeader = new MessageHeader(); 
    private T payload; 
    public MessageHeader getHeaders() { 
     return messageHeader; 
    } 

    public Object getHeader(String key) { 
     return messageHeader.get(key); 
    } 

    public Object addHeader(String key, Object header) { 
     return messageHeader.put(key, header); 
    } 

    public T getPayload() { 
     return payload; 
    } 

    public void setPayload(T messageBody) { 
     this.payload = messageBody; 
    } 
} 

Và tôi đã sử dụng phương pháp sau đây để deserializing payload

public static <T extends Serializable> BasicMessage<T> getConcreteMessageType(String jsonString, Class<T> classType) { 
     try { 
      ObjectMapper mapper = new ObjectMapper(); 
      JavaType javaType = mapper.getTypeFactory().constructParametricType(BasicMessage.class, classType); 
      return mapper.readValue(jsonString, javaType); 
     } catch (IOException e) { 

     } 
} 

nơi jsonString chứa BasicMessageObject trong một chuỗi.

+0

+1 ... bạn sẽ mất tính bất biến của đối tượng đã tạo. Hay điều này cũng có thể xảy ra với những người định cư riêng? – Karussell

1

Chuỗi JSON cần được deserialized sẽ phải chứa thông tin loại về tham số T.
Bạn sẽ phải đặt chú thích Jackson trên mọi lớp có thể được chuyển thành tham số T đến lớp AgentResponse để thông tin loại về loại thông số T có thể được đọc từ/được viết bằng chuỗi JSON bởi Jackson.

Giả sử rằng T có thể là bất kỳ lớp nào mở rộng lớp trừu tượng Result.

public class AgentResponse<T extends Result> { 
    public Hits<T> hits; 
} 

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT) 
@JsonSubTypes({ 
     @JsonSubTypes.Type(value = ImageResult.class, name = "ImageResult"), 
     @JsonSubTypes.Type(value = NewsResult.class, name = "NewsResult")}) 
public abstract class Result { 

} 

public class ImageResult extends Result { 

} 

public class NewsResult extends Result { 

} 

Khi mỗi lớp (hoặc siêu kiểu chung của họ) có thể được thông qua như là tham số T được chú thích, Jackson sẽ bao gồm thông tin về thông số T trong JSON. JSON như vậy có thể được deserialized mà không biết tham số T tại thời gian biên dịch.
Điều này Jackson documentation link nói về Deserialization đa hình nhưng cũng hữu ích để tham khảo cho câu hỏi này.

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