简体   繁体   English

使用Jackson将嵌套数组反序列化为ArrayList

[英]Deserialize nested array as ArrayList with Jackson

I have a piece of JSON, that looks like this: 我有一块JSON,看起来像这样:

{
  "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;
}

And two wrapper classes: 还有两个包装类:

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

public class Response {
    public Authors authors;
}

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. 我想找到一种方法来删除Authors类,并将列表作为Entry类的属性。 Is something like that is possible with Jackson? 杰克逊有可能做到这样吗?

Update 更新

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

And using it like this: 并使用它像这样:

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

Thanks @wassgren for the idea. 谢谢@wassgren的想法。

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 . 第一种是使用Jackson Tree Model( JsonNode ),第二种是使用名为UNWRAP_ROOT_VALUE的反序列化功能。


Alternative 1: Use JsonNode 备选方案1:使用JsonNode

When deserializing JSON using Jackson there are multiple ways to control what type of objects that are to be created. 使用Jackson反序列化JSON时,有多种方法可以控制要创建的对象类型。 The ObjectMapper can deserialize the JSON to eg a Map , JsonNode (via the readTree -method) or a POJO. ObjectMapper可以将JSON反序列化为例如MapJsonNode (通过readTree -method)或POJO。

If you combine the readTree -method with the POJO conversion the wrappers can be completely removed. 如果将readTree -method与POJO转换结合使用,则可以完全删除包装器。 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. 如果使用此树模型方法,则可以完全删除包装类。


Alternative 2: remove one of the wrappers and unwrap the root value The second approach is to remove only one of the wrappers. 备选方案2:删除其中一个包装器并解开根值第二种方法是仅删除其中一个包装器。 Assume that you remove the Authors class but keep the Response -wrapper. 假设您删除了Authors类,但保留了Response -wrapper。 If you add the a @JsonRootName -annotation you can later unwrap the top-level name. 如果添加a @JsonRootName -annotation,稍后可以打开顶级名称。

@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. 第二种方法只删除其中一个包装类,而是解析函数非常漂亮。

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

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