Jackson - 如何处理(反序列化)嵌套的 JSON?

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

我有一个 Vendor 对象,可以从单个“vendor”json 正确反序列化,但我想将其反序列化为一个,我只是不知道如何让 Jackson 合作。有什么提示吗?Vendor[]


答案 1

这是一个粗略但更具声明性的解决方案。我无法将其简化为单个注释,但这似乎效果很好。也不确定大型数据集的性能。

给定此 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);
    }
}

希望这对某人有用!


答案 2

您的数据存在问题,因为数组中有内部包装器对象。据推测,您的对象被设计为处理 、 、 ,但是这些多个对象中的每一个也被包装在具有单个属性的对象中。Vendoridnamecompany_idvendor

我假设您使用的是 Jackson 数据绑定模型。

如果是这样,那么有两件事需要考虑:

第一种方法是使用特殊的 Jackson 配置属性。杰克逊 - 自1.9以来,我相信,如果您使用的是旧版本的杰克逊,这可能不可用 - 提供了UNWRAP_ROOT_VALUE。它适用于将结果包装在要丢弃的顶级单属性对象中的情况。

所以,玩一下:

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

第二种是使用包装对象。即使在丢弃外部包装对象之后,您仍然会遇到将对象包装在单属性对象中的问题。使用包装器来解决这个问题:Vendor

class VendorWrapper
{
    Vendor vendor;

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

类似地,除了使用 ,你还可以定义一个包装类来处理外部对象。假设您有正确的 对象,您可以定义:UNWRAP_ROOT_VALUESVendorVendorWrapper

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 树模型将其解析为 ,丢弃外部节点,并针对 中的每个节点调用:JsonNodesJsonNodeArrayNode

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

这可能会导致更少的代码,但似乎并不比使用两个包装器更笨拙。