2012-07-31 61 views
35
{ 
    vendors: [ 
    { 
     vendor: { 
     id: 367, 
     name: "Kuhn-Pollich", 
     company_id: 1, 
     } 
    }, 
    { 
     vendor: { 
     id: 374, 
     name: "Sawayn-Hermann", 
     company_id: 1, 
     } 
    }] 
} 

Tôi có đối tượng Nhà cung cấp có thể được deserialized từ một "nhà cung cấp" json, nhưng tôi muốn deserialize này vào một Vendor[], tôi chỉ không thể tìm ra cách để làm cho Jackson hợp tác. Có lời khuyên nào không?Jackson - Làm thế nào để xử lý (deserialize) JSON lồng nhau?

+3

Đây là JSON không hợp lệ. 'vendor' có giá trị là một mảng, trong đó có một đối tượng duy nhất và đối tượng đơn lẻ có thuộc tính 'vendor', tiếp theo là một opject mức cao nhất. tức là đối tượng 'vendor' thứ hai không có thuộc tính liên quan trong đối tượng đơn lẻ trong mảng. Hơn nữa, tên thuộc tính không phải là chuỗi, chúng cần được trích dẫn bằng JSON. Tôi đoán rằng bạn đã nhập JSON sai? Một câu trả lời hay sẽ phụ thuộc vào việc biết JSOn bạn đang làm việc với cái gì. – pb2q

+0

Xin lỗi, hãy để tôi chỉnh sửa JSON - Nên sửa ngay bây giờ –

+0

Bạn không thể (hoặc không muốn) để có một lớp Nhà cung cấp có chứa Danh sách ? – hertzsprung

Trả lời

23

Dữ liệu của bạn có vấn đề ở chỗ bạn có các đối tượng bên trong wrapper trong mảng của bạn. Có lẽ đối tượng Vendor của bạn được thiết kế để xử lý id, name, company_id, nhưng mỗi đối tượng trong số đó cũng được bao bọc trong một đối tượng có một thuộc tính duy nhất vendor.

Tôi giả định rằng bạn đang sử dụng mô hình Jackson Data Binding.

Nếu có thì có hai điều cần xem xét:

Đầu tiên là sử dụng thuộc tính cấu hình Jackson đặc biệt. Jackson - kể từ 1.9 Tôi tin rằng, điều này có thể không khả dụng nếu bạn đang sử dụng phiên bản cũ của Jackson - cung cấp UNWRAP_ROOT_VALUE. Nó được thiết kế cho các trường hợp kết quả của bạn được bao bọc trong một đối tượng thuộc tính đơn mức cao nhất mà bạn muốn loại bỏ.

Vì vậy, chơi xung quanh với:

objectMapper.configure(SerializationConfig.Feature.UNWRAP_ROOT_VALUE, true); 

Thứ hai là sử dụng đối tượng bao bọc. Ngay cả sau khi loại bỏ đối tượng bao bọc bên ngoài, bạn vẫn gặp sự cố đối tượng Vendor của bạn được bao bọc trong một đối tượng thuộc tính đơn. Sử dụng một wrapper để làm được việc này:

class VendorWrapper 
{ 
    Vendor vendor; 

    // gettors, settors for vendor if you need them 
} 

Tương tự như vậy, thay vì sử dụng UNWRAP_ROOT_VALUES, bạn cũng có thể định nghĩa một lớp wrapper để xử lý các đối tượng bên ngoài. Giả sử rằng bạn có đúng Vendor, VendorWrapper đối tượng, bạn có thể định nghĩa:

class VendorsWrapper 
{ 
    List<VendorWrapper> vendors = new ArrayList<VendorWrapper>(); 

    // gettors, settors for vendors if you need them 
} 

// in your deserialization code: 
ObjectMapper mapper = new ObjectMapper(); 
JsonNode rootNode = mapper.readValue(jsonInput, VendorsWrapper.class); 

Cây đối tượng cho VendorsWrapper là tương tự như JSON của bạn:

VendorsWrapper: 
    vendors: 
    [ 
     VendorWrapper 
      vendor: Vendor, 
     VendorWrapper: 
      vendor: Vendor, 
     ... 
    ] 

Cuối cùng, bạn có thể sử dụng Jackson Tree Model để phân tích này vào JsonNodes, loại bỏ nút ngoài và cho mỗi JsonNode trong số ArrayNode, gọi:

mapper.readValue(node.get("vendor").getTextValue(), Vendor.class); 

Điều đó có thể dẫn đến ít mã hơn, nhưng có vẻ như không ít vụng về hơn là sử dụng hai trình bao bọc.

+0

Cảm ơn bạn, đây là cách tôi đã làm nó Tôi chỉ hy vọng có một cách tốt hơn. Tôi sẽ đánh dấu nó là chính xác. –

+0

Tôi cũng muốn thấy một giải pháp sử dụng một cái gì đó như UNWRAP_ROOT_VALUES, chỉ có 1 cấp độ sâu hơn, nhưng tôi không nghĩ rằng nó có thể. Một tùy chọn khác của khóa học là sử dụng một deserializer tùy chỉnh, và chỉ cần thêm móc để tìm kiếm các đối tượng thực tế bạn quan tâm và loại bỏ mọi thứ khác, hoặc sử dụng phương pháp Jackson _Tree Model_, vứt bỏ cấp cao nhất 'JsonNode', và lấy các JsonNodes quấn các Nhà cung cấp của bạn, gọi 'getTextValue' và chuyển _that_ thành' mapper.readValue'. Jackson thực sự mang đến cho bạn một loạt các lựa chọn. – pb2q

28

Đây là giải pháp thô lỗ nhưng mang tính khai báo hơn. Tôi đã không thể đưa nó xuống một chú thích duy nhất, nhưng điều này có vẻ hoạt động tốt. Cũng không chắc chắn về hiệu suất trên các tập dữ liệu lớn.

Với JSON này:

{ 
    "list": [ 
     { 
      "wrapper": { 
       "name": "Jack" 
      } 
     }, 
     { 
      "wrapper": { 
       "name": "Jane" 
      } 
     } 
    ] 
} 

Và các đối tượng mô hình:

public class RootObject { 
    @JsonProperty("list") 
    @JsonDeserialize(contentUsing = SkipWrapperObjectDeserializer.class) 
    @SkipWrapperObject("wrapper") 
    public InnerObject[] innerObjects; 
} 

public class InnerObject { 
    @JsonProperty("name") 
    public String name; 
} 

Trường hợp voodoo Jackson được thực hiện như:

@Retention(RetentionPolicy.RUNTIME) 
@JacksonAnnotation 
public @interface SkipWrapperObject { 
    String value(); 
} 

public class SkipWrapperObjectDeserializer extends JsonDeserializer<Object> implements 
     ContextualDeserializer { 
    private Class<?> wrappedType; 
    private String wrapperKey; 

    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, 
      BeanProperty property) throws JsonMappingException { 
     SkipWrapperObject skipWrapperObject = property 
       .getAnnotation(SkipWrapperObject.class); 
     wrapperKey = skipWrapperObject.value(); 
     JavaType collectionType = property.getType(); 
     JavaType collectedType = collectionType.containedType(0); 
     wrappedType = collectedType.getRawClass(); 
     return this; 
    } 

    @Override 
    public Object deserialize(JsonParser parser, DeserializationContext ctxt) 
      throws IOException, JsonProcessingException { 
     ObjectMapper mapper = new ObjectMapper(); 
     ObjectNode objectNode = mapper.readTree(parser); 
     JsonNode wrapped = objectNode.get(wrapperKey); 
     Object mapped = mapIntoObject(wrapped); 
     return mapped; 
    } 

    private Object mapIntoObject(JsonNode node) throws IOException, 
      JsonProcessingException { 
     JsonParser parser = node.traverse(); 
     ObjectMapper mapper = new ObjectMapper(); 
     return mapper.readValue(parser, wrappedType); 
    } 
} 

Hy vọng điều này rất hữu ích cho một ai đó!

+0

+1 cho 'mapIntoObject (JsonNode)' ... vì vậy nếu bạn muốn sử dụng 'readValue' với một' JsonNode' chỉ cần truyền 'của nó qua()' – rakslice

+2

Hoạt động tốt: một sự đơn giản nhỏ tôi muốn đề nghị là 'ObjectMapper. convertValue() ', có thể được sử dụng để thay thế 3 dòng trong' mapIntoObject': 'return mapper.convertValue (nút, wrapType);' – StaxMan

+1

@Patrick, tôi đã thử chạy mã ví dụ này. nhận NPE trong SkipWrapperObjectDeserializer tại wrapType = collectedType.getRawClass(); trong phương thức createContextual(). Bạn có thể vui lòng giúp tôi sửa lỗi này không. Cảm ơn. –

7

@Patrick tôi sẽ cải thiện giải pháp của bạn một chút

@Override 
public Object deserialize(JsonParser jp, DeserializationContext ctxt) 
     throws IOException, JsonProcessingException {   
    ObjectNode objectNode = jp.readValueAsTree(); 
    JsonNode wrapped = objectNode.get(wrapperKey); 
    JsonParser parser = node.traverse(); 
    parser.setCodec(jp.getCodec()); 
    Vendor mapped = parser.readValueAs(Vendor.class); 
    return mapped; 
} 

Nó hoạt động nhanh hơn :)

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