用杰克逊将嵌套数组反序列化为ArrayList [英] Deserialize nested array as ArrayList with Jackson

查看:115
本文介绍了用杰克逊将嵌套数组反序列化为ArrayList的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一段JSON,看起来像这样:

I have a piece of JSON, that looks like this:

{
  "authors": {
    "author": [
      {
        "given-name": "Adrienne H.",
        "surname": "Kovacs"
      },
      {
        "given-name": "Philip",
        "surname": "Moons"
      }
    ]
   }
 }

我创建了一个用于存储作者信息的类:

I have created a class to store Author information:

public class Author {
    @JsonProperty("given-name")
    public String givenName;
    public String surname;
}

和两个包装器类:

public class Authors {
    public List<Author> author;
}

public class Response {
    public Authors authors;
}

这是可行的,但是似乎不需要两个包装器类.我想找到一种方法来删除Authors类,并具有一个列表作为Entry类的属性.杰克逊有可能做到这一点吗?

This is working, but having two wrapper classes seems to be unnecessary. I want to find a way to remove Authors class and have a list as a property of Entry class. Is something like that is possible with Jackson?

使用自定义反序列化器解决了该问题:

Solved that with custom deserializer:

public class AuthorArrayDeserializer extends JsonDeserializer<List<Author>> {

    private static final String AUTHOR = "author";
    private static final ObjectMapper mapper = new ObjectMapper();
    private static final CollectionType collectionType =
            TypeFactory
            .defaultInstance()
            .constructCollectionType(List.class, Author.class);

    @Override
    public List<Author> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
            throws IOException, JsonProcessingException {

        ObjectNode objectNode = mapper.readTree(jsonParser);
        JsonNode nodeAuthors = objectNode.get(AUTHOR);

        if (null == nodeAuthors                     // if no author node could be found
                || !nodeAuthors.isArray()           // or author node is not an array
                || !nodeAuthors.elements().hasNext())   // or author node doesn't contain any authors
            return null;

        return mapper.reader(collectionType).readValue(nodeAuthors);
    }
}

并像这样使用它:

@JsonDeserialize(using = AuthorArrayDeserializer.class)
public void setAuthors(List<Author> authors) {
    this.authors = authors;
}

感谢@wassgren的想法.

Thanks @wassgren for the idea.

推荐答案

如果要摆脱包装类,我至少看到两种方法.第一种是使用Jackson树模型(JsonNode),第二种是使用称为UNWRAP_ROOT_VALUE的反序列化功能.

I see at least two approaches to do this if you want to get rid of wrapper classes. The first is to use the Jackson Tree Model (JsonNode) and the second is to use a deserialization feature called UNWRAP_ROOT_VALUE.

替代方法1:使用JsonNode

Alternative 1: Use JsonNode

使用杰克逊反序列化JSON时,有多种方法可以控制要创建的对象类型. ObjectMapper可以将JSON反序列化为例如MapJsonNode(通过readTree方法)或POJO.

When deserializing JSON using Jackson there are multiple ways to control what type of objects that are to be created. The ObjectMapper can deserialize the JSON to e.g. a Map, JsonNode (via the readTree-method) or a POJO.

如果将readTree方法与POJO转换结合使用,则可以完全删除包装器.示例:

If you combine the readTree-method with the POJO conversion the wrappers can be completely removed. Example:

// The author class (a bit cleaned up)
public class Author {
    private final String givenName;
    private final String surname;

    @JsonCreator
    public Author(
            @JsonProperty("given-name") final String givenName,
            @JsonProperty("surname") final String surname) {

        this.givenName = givenName;
        this.surname = surname;
    }

    public String getGivenName() {
        return givenName;
    }

    public String getSurname() {
        return surname;
    }
}

反序列化可以看起来像这样:

The deserialization can then look something like this:

// The JSON
final String json = "{\"authors\":{\"author\":[{\"given-name\":\"AdrienneH.\",\"surname\":\"Kovacs\"},{\"given-name\":\"Philip\",\"surname\":\"Moons\"}]}}";

ObjectMapper mapper = new ObjectMapper();

// Read the response as a tree model
final JsonNode response = mapper.readTree(json).path("authors").path("author");

// Create the collection type (since it is a collection of Authors)
final CollectionType collectionType =
        TypeFactory
                .defaultInstance()
                .constructCollectionType(List.class, Author.class);

// Convert the tree model to the collection (of Author-objects)
List<Author> authors = mapper.reader(collectionType).readValue(response);

// Now the authors-list is ready to use...

如果使用此树模型方法,则可以完全删除包装器类.

If you use this Tree Model-approach the wrapper classes can be completely removed.

替代方法2:删除其中一个包装器,然后解开根值 第二种方法是仅除去其中一个包装.假设您删除了Authors类,但保留了Response -wrapper.如果添加@JsonRootName批注,则以后可以解开顶级名称.

Alternative 2: remove one of the wrappers and unwrap the root value The second approach is to remove only one of the wrappers. Assume that you remove the Authors class but keep the Response-wrapper. If you add the a @JsonRootName-annotation you can later unwrap the top-level name.

@JsonRootName("authors") // This is new compared to your example
public class Response {
    private final List<Author> authors;

    @JsonCreator
    public Response(@JsonProperty("author") final List<Author> authors) {
        this.authors = authors;
    }

    @JsonProperty("author")
    public List<Author> getAuthors() {
        return authors;
    }
}

然后,对于您的映射器,只需使用:

Then, for your mapper simply use:

ObjectMapper mapper = new ObjectMapper();

// Unwrap the root value i.e. the "authors"
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
final Response responsePojo = mapper.readValue(json, Response.class);

第二种方法只删除了一个包装器类,但是解析功能却很漂亮.

The second approach only removes one of the wrapper classes but instead the parsing function is quite pretty.

这篇关于用杰克逊将嵌套数组反序列化为ArrayList的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆