简体   繁体   English

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

[英]Jackson - How to process (deserialize) nested JSON?

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

I have a Vendor object that can properly be deserialized from a single "vendor" json, but I want to deserialize this into a Vendor[] , I just can't figure out how to make Jackson cooperate.我有一个可以从单个“供应商”json 正确反序列化的 Vendor 对象,但我想将其反序列化为Vendor[] ,我就是不知道如何让 Jackson 合作。 Any tips?有小费吗?

Here is a rough but more declarative solution.这是一个粗略但更具声明性的解决方案。 I haven't been able to get it down to a single annotation, but this seems to work well.我无法将其归结为单个注释,但这似乎运行良好。 Also not sure about performance on large data sets.也不确定大型数据集的性能。

Given this JSON:鉴于此 JSON:

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

And these model objects:而这些模型对象:

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

and

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

Where the Jackson voodoo is implemented like:杰克逊伏都教的实施方式如下:

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

and

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

Hope this is useful to someone!希望这对某人有用!

Your data is problematic in that you have inner wrapper objects in your array.您的数据有问题,因为您的数组中有内部包装对象。 Presumably your Vendor object is designed to handle id , name , company_id , but each of those multiple objects are also wrapped in an object with a single property vendor .据推测,您的Vendor对象旨在处理idnamecompany_id ,但这些多个对象中的每一个也包含在一个具有单个属性vendor的对象中。

I'm assuming that you're using the Jackson Data Binding model.我假设您正在使用 Jackson 数据绑定模型。

If so then there are two things to consider:如果是这样,那么有两件事需要考虑:

The first is using a special Jackson config property.第一个是使用特殊的 Jackson 配置属性。 Jackson - since 1.9 I believe, this may not be available if you're using an old version of Jackson - provides UNWRAP_ROOT_VALUE . Jackson - 我相信从 1.9 开始,如果您使用的是旧版本的 Jackson,这可能不可用 - 提供UNWRAP_ROOT_VALUE It's designed for cases where your results are wrapped in a top-level single-property object that you want to discard.它专为您的结果包含在您想要丢弃的顶级单一属性对象中的情况而设计。

So, play around with:所以,玩玩:

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

The second is using wrapper objects.第二个是使用包装对象。 Even after discarding the outer wrapper object you still have the problem of your Vendor objects being wrapped in a single-property object.即使在丢弃外部包装对象之后,您仍然会遇到Vendor对象被包装在单一属性对象中的问题。 Use a wrapper to get around this:使用包装器来解决这个问题:

class VendorWrapper
{
    Vendor vendor;

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

Similarly, instead of using UNWRAP_ROOT_VALUES , you could also define a wrapper class to handle the outer object.同样,您也可以定义一个包装类来处理外部对象,而不是使用UNWRAP_ROOT_VALUES Assuming that you have correct Vendor , VendorWrapper object, you can define:假设您有正确的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); 

The object tree for VendorsWrapper is analogous to your JSON: VendorsWrapper 的对象树类似于您的 JSON:

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

Finally, you might use the Jackson Tree Model to parse this into JsonNodes , discarding the outer node, and for each JsonNode in the ArrayNode , calling:最后,您可以使用杰克逊树模型来解析这个进入JsonNodes ,丢弃外侧的节点,并为每个JsonNodeArrayNode ,美其名曰:

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

That might result in less code, but it seems no less clumsy than using two wrappers.这可能会导致更少的代码,但它似乎并不比使用两个包装器更笨拙。

@Patrick I would improve your solution a bit @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;
}

It works faster :)它工作得更快:)

I'm quite late to the party, but one approach is to use a static inner class to unwrap values:我参加聚会已经很晚了,但一种方法是使用静态内部类来解包值:

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

class Scratch {
    private final String aString;
    private final String bString;
    private final String cString;
    private final static String jsonString;

    static {
        jsonString = "{\n" +
                "  \"wrap\" : {\n" +
                "    \"A\": \"foo\",\n" +
                "    \"B\": \"bar\",\n" +
                "    \"C\": \"baz\"\n" +
                "  }\n" +
                "}";
    }

    @JsonCreator
    Scratch(@JsonProperty("A") String aString,
            @JsonProperty("B") String bString,
            @JsonProperty("C") String cString) {
        this.aString = aString;
        this.bString = bString;
        this.cString = cString;
    }

    @Override
    public String toString() {
        return "Scratch{" +
                "aString='" + aString + '\'' +
                ", bString='" + bString + '\'' +
                ", cString='" + cString + '\'' +
                '}';
    }

    public static class JsonDeserializer {
        private final Scratch scratch;

        @JsonCreator
        public JsonDeserializer(@JsonProperty("wrap") Scratch scratch) {
            this.scratch = scratch;
        }

        public Scratch getScratch() {
            return scratch;
        }
    }

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        Scratch scratch = objectMapper.readValue(jsonString, Scratch.JsonDeserializer.class).getScratch();
        System.out.println(scratch.toString());
    }
}

However, it's probably easier to use objectMapper.configure(SerializationConfig.Feature.UNWRAP_ROOT_VALUE, true);但是,使用objectMapper.configure(SerializationConfig.Feature.UNWRAP_ROOT_VALUE, true);可能更容易objectMapper.configure(SerializationConfig.Feature.UNWRAP_ROOT_VALUE, true); in conjunction with @JsonRootName("aName") , as pointed out by pb2q结合@JsonRootName("aName")正如 pb2q 所指出的

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM