如何使用 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 元素。

也许这只是将简单的 Maps 与 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);
}