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

我有一個可以適當地從單一的「供應商」反序列化JSON賣方的對象,但我想反序列化到一個Vendor[]這個,我只是無法弄清楚如何使傑克遜合作。有小費嗎?傑克遜 - 如何處理(反序列化)嵌套的JSON?

+3

這是無效的JSON。 'vendor'有一個數組,它有一個單一的對象,單個對象有一個'vendor'屬性,然後是一個光禿的頂級對象。即第二個'vendor'對象在數組中的單個對象中沒有關聯的屬性。此外,屬性名稱不是字符串,它們需要用JSON引用。我猜你已經輸入了錯誤的JSON?一個好的答案將取決於你知道你實際使用的是什麼樣的JSOn。 – pb2q 2012-07-31 19:24:52

+0

對不起,讓我糾正JSON - 現在應該修復 – 2012-07-31 19:50:00

+0

您無法(或不想)擁有包含列表的供應商類? – hertzsprung 2012-07-31 20:05:34

回答

23

您的數據存在問題,因爲您的數組中包含內部包裝對象。假設您的Vendor對象設計爲處理id,name,company_id,但這些多個對象中的每一個也都包含在具有單個屬性vendor的對象中。

我假設你正在使用Jackson Data Binding模型。

如果是的話,需要考慮兩件事情:

首先是採用了特殊的傑克遜config屬性。傑克遜 - 自1.9以來,我相信,如果您使用舊版本的傑克遜,這可能不可用 - 提供UNWRAP_ROOT_VALUE。它適用於將結果包裝在要放棄的頂級單一屬性對象中的情況。

所以,玩弄:

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

第二是使用包裝對象。即使在丟棄外部包裝對象後,仍然存在將對象包裝在單個屬性對象中的問題。使用的包裝來解決這個問題:

class VendorWrapper 
{ 
    Vendor vendor; 

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

類似的,而不是使用UNWRAP_ROOT_VALUES,你也可以定義一個包裝類來處理外部對象。假設你有正確的VendorVendorWrapper對象,你可以定義:

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

爲VendorsWrapper對象樹類似於你的JSON:

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

最後,您可以使用傑克遜Tree Model解析這個成JsonNodes,丟棄所述外節點,以及用於在每個ArrayNodeJsonNode,調用:

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

這可能會導致較少的代碼,但似乎不會比使用兩個包裝器笨拙。

+0

謝謝,我就是這麼做的,我只是希望有更好的辦法。我會將其標記爲正確的。 – 2012-08-01 19:10:06

+0

我也想看看使用UNWRAP_ROOT_VALUES這樣的解決方案,只有更深的1級,但我認爲這是不可能的。另一個選項當然是使用自定義的反序列化器,並且只需添加鉤子來查找您感興趣的實際對象並放棄其他所有內容,或者使用Jackson _Tree Model_方法,丟棄頂層'JsonNode',並採取包裝你的供應商的JsonNode,調用'getTextValue',並將_that_傳遞給'mapper.readValue'。傑克遜真的給你一個選擇的過剩。 – pb2q 2012-08-01 19:16:26

28

這是一個粗糙但更具說明性的解決方案。我一直無法把它歸結爲一個單一的註釋,但這似乎運作良好。也不確定大數據集的性能。

鑑於這種JSON:

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

而且這些模型對象:

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

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

當傑克遜被巫術等實現:

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

希望這是有用的人!

+0

對於mapIntoObject(JsonNode)+1 ......所以如果你想通過'JsonNode'使用'readValue'只是傳遞'traverse()' – rakslice 2014-10-24 20:01:05

+2

工作得很好:我建議的一個小的簡化是ObjectMapper。 convertValue()',它可以用來替換'mapIntoObject'中的3行:'return mapper.convertValue(node,wrappedType);' – StaxMan 2014-12-18 05:06:40

+1

@Patrick,我試着運行這個示例代碼。在SkipWrapperObjectDeserializer中取得NPE wrappedType = collectedType.getRawClass();在createContextual()方法中使用 。你能幫我解決這個問題嗎?謝謝。 – 2016-07-30 23:06:27

7

@Patrick 我會改善你的解決方案有點

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

它的工作原理快:)