如何使用 Jackson Annotations 处理具有随机对象名称的 JSON
How to use Jackson Annotations to process JSON with random object names
我有以下 JSON:
{
"animals": {
"113110": {
"id": 113110,
"name": "Dog",
.....
},
"121853": {
"id": 121853,
"name": "Cat",
.....
}
}
}
理想情况下,JSON 应该如下所示,实现 Jackson 注释将是微不足道的:
{
"animals": [
{
"id": 113110,
"name": "Dog",
.....
},
{
"id": 121853,
"name": "Cat",
.....
}
]
}
但是,有没有办法使用 Jackson 来抽象对象名称,这样我就可以使用原始的 JSON,如果有人明白我的意思?
编辑:
我不知道如何创建我的 POJO。我可以用对象 113110 和 121853 创建一个 Animal class,但由于这些对象总是不同的,我如何在我的 Animal class 中使用 Jackson 注释以便我可以反序列化 JSON ?
就个人而言,每当我处理无法轻松映射到 POJO 的怪异 JSON 时,我只是进行自定义序列化。
我可能会让 POJO 看起来像这样:
public class Animal
{
String id;
String name;
}
public class JsonThing
{
List<Animal> animals;
}
然后我将使用 Jackson 流 API 实现自定义解析器。这是 JsonDeserializer<JsonThing>
:
的快速存根
public Stuff deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
.... // Start by creating a JsonThing instance and init the list.
while (jp.nextToken() != JsonToken.END_OBJECT)
{
jp.nextToken();
switch (jp.getCurrentName())
{
case "animals":
jp.nextToken(); // Skip to {
jp.nextToken(); // Skip id field
Animal a = jp.readValuesAs(Animal.class);
// Add to list
}
}
..... // Return JsonThing
}
如果事先不知道密钥,则使用 Map 而不是 POJO。
看看Example 1 and Example 2
你可以尝试任何一个。
示例代码:(使用 Jackson 库)
TypeReference<Map<String, Object>> typeRef = new TypeReference<Map<String, Object>>() {};
ObjectMapper mapper = new ObjectMapper();
try {
Map<String, Object> data = mapper.readValue(jsonString, typeRef);
} catch (Exception e) {
System.out.println("There might be some issue with the JSON string");
}
示例代码:使用GSON Library
Type type = new TypeToken<Map<String, Object>>() {}.getType();
Map<String, Object> data = new Gson().fromJson(jsonString, type);
根据 OP 示例,当您的 JSON 是 "dynamic" 时,总是有点混乱。主要方法是
- 将 JSON 解析为某种动态
Map
-结构
- 将 JSON 解析为 树 结构(即
JsonNode
)
- 使用自定义解串器解析 JSON 并将其映射到 POJO
所有这些方法都有缺点。 Map
-方法不提供类型安全,并且在遍历对象结构时不提供太多功能。
JsonNode
方法提供了一些不错的类型方法和一些遍历方法。在我看来,这是一种比 Map
方法更简洁的方法。
POJO 方法是类型安全的,但需要自定义反序列化器,这通常不太好...
所以,也许下面的"hybrid"方法会有用。
// Setup the mapper
final ObjectMapper mapper = new ObjectMapper();
// Parse the json to a tree (JsonNode). This is IMO nicer than the
// Map since it exposes some nice methods for managing the
// underlying data
final JsonNode json = mapper.readTree(jsonString);
// Alt 1, use JsonNode directly
for (final JsonNode animal : json.path("animals")) {
final int id = animal.get("id").asInt();
final String name = animal.get("name").asText();
// Do stuff with name and id...
}
如果 JsonNode
方法感觉有点过于 raw 那么可以在不使用反序列化器的情况下将 JsonNode
对象转换为 POJO .如果您假设以下 POJO:
public class Animal {
private final int id;
private final String name;
@JsonCreator
public Animal(@JsonProperty("id") final int id, @JsonProperty("name") final String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
}
然后,此代码可用于转换为 POJO:
final ObjectMapper mapper = new ObjectMapper();
final JsonNode json = mapper.readTree(jsonString);
// Alt 2, convert to a Pojo
for (final JsonNode animal : json.path("animals")) {
final Animal a = mapper.treeToValue(animal, Animal.class);
// Handle the animal instance...
}
最后,如果 POJO 仍然包含动态数据,您可以使用以下方法来处理它。在您的 POJO 中,声明以下内容:
private final Map<String, Object> dynamic = new HashMap<>();
@JsonAnySetter
private void set(String name, Object value) {
dynamic.put(name, value);
}
请注意,方法不是必须 public
(即它可以对 外界 隐藏)。这样你就可以得到所有 unknown/dynamic JSON 元素。
也许这只是将简单的 Map
s 与 pojos 相结合的问题?喜欢:
public class Wrapper {
public Map<Long, Animal> animals;
}
public class Animal {
public long id;
public String name;
}
就是这样;尽管那里的 ID 匹配,但也许没有必要尝试对这种依赖性进行建模。
谢谢大家,但我无法真正理解其余的答案(我真的不想深入研究 Jackson,我只想将其转换为 POJO),所以我找到了替代解决方案。
我遗漏了一个关键信息:我发布的 JSON 是一个更大的 JSON 对象的一部分。
我最终使用了 Jackson 的 @AnySetter,因为我注意到任何与 "animals" 相关的 "un-parsable" JSON 数据都可以在其父 [=22= 中如下定义的 additionalProperties 中检索]:
public class AnimalParent {
@JsonIgnore
private Animal animal;
@JsonIgnore
private Map<String, Object> additionalProperties =
new HashMap<String, Object>();
public Animal getAnimal() {
return this.animal;
}
public void setAnimal(Animal animal) {
this.animal = animal;
}
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
然后在我解析父JSON的主要方法中,解析完成后我有以下内容来解析动物。
// start parsing parent JSON
...
// end parsing parent JSON
// parse animal
ObjectMapper mapper = new ObjectMapper();
if (animalParent.getAdditionalProperties() != null) {
for (Map.Entry<String, Object> item : animalParent
.getAdditionalProperties().entrySet()) {
Animal animal = mapper.convertValue(item.getValue(), Animal.class);
animalParent.setAnimal(animal);
}
我有以下 JSON:
{
"animals": {
"113110": {
"id": 113110,
"name": "Dog",
.....
},
"121853": {
"id": 121853,
"name": "Cat",
.....
}
}
}
理想情况下,JSON 应该如下所示,实现 Jackson 注释将是微不足道的:
{
"animals": [
{
"id": 113110,
"name": "Dog",
.....
},
{
"id": 121853,
"name": "Cat",
.....
}
]
}
但是,有没有办法使用 Jackson 来抽象对象名称,这样我就可以使用原始的 JSON,如果有人明白我的意思?
编辑:
我不知道如何创建我的 POJO。我可以用对象 113110 和 121853 创建一个 Animal class,但由于这些对象总是不同的,我如何在我的 Animal class 中使用 Jackson 注释以便我可以反序列化 JSON ?
就个人而言,每当我处理无法轻松映射到 POJO 的怪异 JSON 时,我只是进行自定义序列化。
我可能会让 POJO 看起来像这样:
public class Animal
{
String id;
String name;
}
public class JsonThing
{
List<Animal> animals;
}
然后我将使用 Jackson 流 API 实现自定义解析器。这是 JsonDeserializer<JsonThing>
:
public Stuff deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
.... // Start by creating a JsonThing instance and init the list.
while (jp.nextToken() != JsonToken.END_OBJECT)
{
jp.nextToken();
switch (jp.getCurrentName())
{
case "animals":
jp.nextToken(); // Skip to {
jp.nextToken(); // Skip id field
Animal a = jp.readValuesAs(Animal.class);
// Add to list
}
}
..... // Return JsonThing
}
如果事先不知道密钥,则使用 Map 而不是 POJO。
看看Example 1 and Example 2
你可以尝试任何一个。
示例代码:(使用 Jackson 库)
TypeReference<Map<String, Object>> typeRef = new TypeReference<Map<String, Object>>() {};
ObjectMapper mapper = new ObjectMapper();
try {
Map<String, Object> data = mapper.readValue(jsonString, typeRef);
} catch (Exception e) {
System.out.println("There might be some issue with the JSON string");
}
示例代码:使用GSON Library
Type type = new TypeToken<Map<String, Object>>() {}.getType();
Map<String, Object> data = new Gson().fromJson(jsonString, type);
根据 OP 示例,当您的 JSON 是 "dynamic" 时,总是有点混乱。主要方法是
- 将 JSON 解析为某种动态
Map
-结构 - 将 JSON 解析为 树 结构(即
JsonNode
) - 使用自定义解串器解析 JSON 并将其映射到 POJO
所有这些方法都有缺点。 Map
-方法不提供类型安全,并且在遍历对象结构时不提供太多功能。
JsonNode
方法提供了一些不错的类型方法和一些遍历方法。在我看来,这是一种比 Map
方法更简洁的方法。
POJO 方法是类型安全的,但需要自定义反序列化器,这通常不太好...
所以,也许下面的"hybrid"方法会有用。
// Setup the mapper
final ObjectMapper mapper = new ObjectMapper();
// Parse the json to a tree (JsonNode). This is IMO nicer than the
// Map since it exposes some nice methods for managing the
// underlying data
final JsonNode json = mapper.readTree(jsonString);
// Alt 1, use JsonNode directly
for (final JsonNode animal : json.path("animals")) {
final int id = animal.get("id").asInt();
final String name = animal.get("name").asText();
// Do stuff with name and id...
}
如果 JsonNode
方法感觉有点过于 raw 那么可以在不使用反序列化器的情况下将 JsonNode
对象转换为 POJO .如果您假设以下 POJO:
public class Animal {
private final int id;
private final String name;
@JsonCreator
public Animal(@JsonProperty("id") final int id, @JsonProperty("name") final String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
}
然后,此代码可用于转换为 POJO:
final ObjectMapper mapper = new ObjectMapper();
final JsonNode json = mapper.readTree(jsonString);
// Alt 2, convert to a Pojo
for (final JsonNode animal : json.path("animals")) {
final Animal a = mapper.treeToValue(animal, Animal.class);
// Handle the animal instance...
}
最后,如果 POJO 仍然包含动态数据,您可以使用以下方法来处理它。在您的 POJO 中,声明以下内容:
private final Map<String, Object> dynamic = new HashMap<>();
@JsonAnySetter
private void set(String name, Object value) {
dynamic.put(name, value);
}
请注意,方法不是必须 public
(即它可以对 外界 隐藏)。这样你就可以得到所有 unknown/dynamic JSON 元素。
也许这只是将简单的 Map
s 与 pojos 相结合的问题?喜欢:
public class Wrapper {
public Map<Long, Animal> animals;
}
public class Animal {
public long id;
public String name;
}
就是这样;尽管那里的 ID 匹配,但也许没有必要尝试对这种依赖性进行建模。
谢谢大家,但我无法真正理解其余的答案(我真的不想深入研究 Jackson,我只想将其转换为 POJO),所以我找到了替代解决方案。
我遗漏了一个关键信息:我发布的 JSON 是一个更大的 JSON 对象的一部分。
我最终使用了 Jackson 的 @AnySetter,因为我注意到任何与 "animals" 相关的 "un-parsable" JSON 数据都可以在其父 [=22= 中如下定义的 additionalProperties 中检索]:
public class AnimalParent {
@JsonIgnore
private Animal animal;
@JsonIgnore
private Map<String, Object> additionalProperties =
new HashMap<String, Object>();
public Animal getAnimal() {
return this.animal;
}
public void setAnimal(Animal animal) {
this.animal = animal;
}
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
然后在我解析父JSON的主要方法中,解析完成后我有以下内容来解析动物。
// start parsing parent JSON
...
// end parsing parent JSON
// parse animal
ObjectMapper mapper = new ObjectMapper();
if (animalParent.getAdditionalProperties() != null) {
for (Map.Entry<String, Object> item : animalParent
.getAdditionalProperties().entrySet()) {
Animal animal = mapper.convertValue(item.getValue(), Animal.class);
animalParent.setAnimal(animal);
}