使用 Jackson 将嵌套数组反序列化为 ArrayList
Deserialize nested array as ArrayList with Jackson
我有一块 JSON,看起来像这样:
{
"authors": {
"author": [
{
"given-name": "Adrienne H.",
"surname": "Kovacs"
},
{
"given-name": "Philip",
"surname": "Moons"
}
]
}
}
我创建了一个class来存储作者信息:
public class Author {
@JsonProperty("given-name")
public String givenName;
public String surname;
}
和两个包装器 classes:
public class Authors {
public List<Author> author;
}
public class Response {
public Authors authors;
}
这是有效的,但是有两个包装器 classes 似乎是不必要的。我想找到一种方法来删除 Authors
class 并将列表作为条目 class 的 属性。 Jackson 可以做这样的事情吗?
更新
使用自定义解串器解决了这个问题:
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 的想法。
如果您想摆脱包装 classes,我看到至少有两种方法可以做到这一点。第一个是使用杰克逊树模型 (JsonNode
),第二个是使用称为 UNWRAP_ROOT_VALUE
.
的反序列化功能
备选方案 1:使用 JsonNode
使用 Jackson 反序列化 JSON 时,有多种方法可以控制要创建的对象类型。 ObjectMapper
可以将 JSON 反序列化为例如Map
、JsonNode
(通过 readTree
方法)或 POJO。
如果将 readTree
方法与 POJO
转换结合使用,则可以完全删除包装器。示例:
// 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 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...
如果您使用这种树模型方法,包装 classes 可以完全删除。
备选方案 2:删除其中一个包装器并解开根值
第二种方法是只删除一个包装器。假设您删除 Authors
class 但保留 Response
-wrapper。如果您添加 @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;
}
}
然后,对于您的映射器,只需使用:
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);
第二种方法只删除了一个包装器class,但解析函数却相当漂亮。
我有一块 JSON,看起来像这样:
{
"authors": {
"author": [
{
"given-name": "Adrienne H.",
"surname": "Kovacs"
},
{
"given-name": "Philip",
"surname": "Moons"
}
]
}
}
我创建了一个class来存储作者信息:
public class Author {
@JsonProperty("given-name")
public String givenName;
public String surname;
}
和两个包装器 classes:
public class Authors {
public List<Author> author;
}
public class Response {
public Authors authors;
}
这是有效的,但是有两个包装器 classes 似乎是不必要的。我想找到一种方法来删除 Authors
class 并将列表作为条目 class 的 属性。 Jackson 可以做这样的事情吗?
更新
使用自定义解串器解决了这个问题:
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 的想法。
如果您想摆脱包装 classes,我看到至少有两种方法可以做到这一点。第一个是使用杰克逊树模型 (JsonNode
),第二个是使用称为 UNWRAP_ROOT_VALUE
.
备选方案 1:使用 JsonNode
使用 Jackson 反序列化 JSON 时,有多种方法可以控制要创建的对象类型。 ObjectMapper
可以将 JSON 反序列化为例如Map
、JsonNode
(通过 readTree
方法)或 POJO。
如果将 readTree
方法与 POJO
转换结合使用,则可以完全删除包装器。示例:
// 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 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...
如果您使用这种树模型方法,包装 classes 可以完全删除。
备选方案 2:删除其中一个包装器并解开根值
第二种方法是只删除一个包装器。假设您删除 Authors
class 但保留 Response
-wrapper。如果您添加 @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;
}
}
然后,对于您的映射器,只需使用:
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);
第二种方法只删除了一个包装器class,但解析函数却相当漂亮。