web-dev-qa-db-ja.com

ジャクソン-ネストされたJSONを処理(デシリアライズ)する方法は?

{
  vendors: [
    {
      vendor: {
        id: 367,
        name: "Kuhn-Pollich",
        company_id: 1,
      }
    },
    {
      vendor: {
        id: 374,
        name: "Sawayn-Hermann",
        company_id: 1,
      }
  }]
}

単一の「ベンダー」jsonから適切にデシリアライズできるVendorオブジェクトがありますが、これをVendor[]にデシリアライズしたいのですが、ジャクソンを協力させる方法がわかりません。任意のヒント?

40
Sam Stern

配列に内部wrapperオブジェクトがあるという点で、データには問題があります。おそらく、Vendorオブジェクトはidnamecompany_idを処理するように設計されていますが、これらの複数のオブジェクトはそれぞれ、単一のプロパティvendorを持つオブジェクトにラップされています。

Jackson Data Binding モデルを使用していると仮定しています。

その場合、考慮すべき2つのことがあります。

1つ目は、特別なJackson構成プロパティを使用することです。ジャクソン-1.9以降、古いバージョンのジャクソンを使用している場合、これは利用できない可能性があります- UNWRAP_ROOT_VALUE を提供します。結果が、破棄するトップレベルの単一プロパティオブジェクトにラップされている場合のために設計されています。

だから、遊んでください:

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

2つ目は、ラッパーオブジェクトの使用です。外側のラッパーオブジェクトを破棄した後でも、Vendorオブジェクトが単一プロパティオブジェクトにラップされるという問題があります。これを回避するにはラッパーを使用します。

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,
        ...
    ]

最後に、Jackson Tree Model を使用して、これをJsonNodesに解析し、外部ノードを破棄し、各JsonNodeについてArrayNode、呼び出し:

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

その結果、コードが少なくなる可能性がありますが、2つのラッパーを使用するよりも不器用ではないようです。

26
pb2q

ラフですが、より宣言的なソリューションです。単一のアノテーションにまとめることはできませんでしたが、これはうまくいくようです。また、大規模なデータセットのパフォーマンスについてもわかりません。

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

これが誰かに役立つことを願っています!

32
Patrick

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

より速く動作します:)

11
Alexey