次のようなJSONがあります。
{
"authors": {
"author": [
{
"given-name": "Adrienne H.",
"surname": "Kovacs"
},
{
"given-name": "Philip",
"surname": "Moons"
}
]
}
}
著者情報を保存するクラスを作成しました:
public class Author {
@JsonProperty("given-name")
public String givenName;
public String surname;
}
そして、2つのラッパークラス:
public class Authors {
public List<Author> author;
}
public class Response {
public Authors authors;
}
これは機能していますが、2つのラッパークラスを持つ必要はないようです。 Authors
クラスを削除し、Entryクラスのプロパティとしてリストを持つ方法を見つけたいです。ジャクソンではそのようなことが可能ですか?
カスタムデシリアライザーでそれを解決しました:
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のアイデアに感謝します。
ラッパークラスを削除する場合は、少なくとも2つのアプローチがあります。 1つはJackson Tree Model(JsonNode
)を使用することで、2つ目はUNWRAP_ROOT_VALUE
と呼ばれる逆シリアル化機能を使用することです。
代替1:JsonNode
を使用
Jacksonを使用してJSONをデシリアライズする場合、作成するオブジェクトのタイプを制御する方法は複数あります。 ObjectMapper
はJSONを逆シリアル化できます。 Map
、JsonNode
(readTree
- method経由)またはPOJO。
readTree
- methodと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...
このツリーモデルアプローチを使用すると、ラッパークラスを完全に削除できます。
代替案2:ラッパーの1つを削除し、ルート値をアンラップする 2番目のアプローチは、ラッパーの1つのみを削除することです。 Authors
クラスは削除するが、Response
- wrapperは保持するものとします。 @JsonRootName
-アノテーションを追加すると、後でトップレベルの名前を展開できます。
@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);
2番目のアプローチはラッパークラスの1つを削除するだけですが、代わりに解析関数は非常にきれいです。