如何使用Jackson Annotations处理具有随机对象名称的JSON

时间:2015-02-26 04:02:55

标签: json jackson

我有以下JSON:

{
    "animals": {
        "113110": {
            "id": 113110,
            "name": "Dog",
            .....
        },
        "121853": {
            "id": 121853,
            "name": "Cat",
            .....
        }
    }
}

理想情况下,JSON应如下所示,实施杰克逊注释将是微不足道的:

{
    "animals": [
        {
            "id": 113110,
            "name": "Dog",
            .....
        },
        {
            "id": 121853,
            "name": "Cat",
            .....
        }
    ]
}

但是,有没有办法使用Jackson来抽象对象名称,这样我就可以使用原始的JSON,如果有人知道我的意思?

编辑:

我不知道如何创建我的POJO。我可以用对象113110和121853创建一个Animal类,但由于这些对象总是不同,我如何在我的Animal类中使用Jackson注释以便我可以反序列化JSON?

5 个答案:

答案 0 :(得分:3)

谢谢大家,但我真的不能理解其余的答案(我真的不想深入研究杰克逊,我只是想把它转换为POJO),所以我找到了另一种解决方案。

我遗漏了一些关键信息:我发布的JSON是一个更大的JSON对象的一部分。

我最终使用了Jackson的@AnySetter,因为我注意到任何与“animals”相关的“不可解析”的JSON数据都可以在其父类中定义如下的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的main方法中,在完成解析后解析动物后我有以下内容。

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

答案 1 :(得分:1)

就个人而言,只要我处理不易映射到POJO的怪异JSON,我就会进行自定义序列化。

我可能会让POJO看起来像这样:

public class Animal
{
    String id;
    String name;
}

public class JsonThing
{
    List<Animal> animals;
}

然后我将使用Jackson stream 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
}

答案 2 :(得分:1)

如果事先不知道密钥,请使用地图代替POJO。

查看Example 1Example 2

你可以尝试任何一个。

示例代码:(使用 Jackson Library

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

答案 3 :(得分:1)

根据OP示例,当您的JSON是“动态”时总是有点乱。主要方法是

  • 将JSON解析为某种动态Map - 结构
  • 将JSON解析为 -structure(即JsonNode
  • 使用自定义反序列化程序解析JSON并将其映射到POJO

所有这些方法都有缺点。 Map - 方法不提供类型安全性,并且在遍历对象结构时不提供太多功能。

JsonNode方法提供了一些不错的类型方法和一些遍历方法。 IMO这是一种比Map方法更清晰的方法。

POJO方法是类型安全的,但是需要自定义反序列化器,这通常不太漂亮......

因此,也许可以使用以下“混合”方法。

// 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(即它可以从外部世界隐藏)。通过这种方式,您将获得所有未知/动态JSON元素。

答案 4 :(得分:0)

或许这只是将简单的Map与pojos结合起来的问题?像:

public class Wrapper {
  public Map<Long, Animal> animals;
}

public class Animal {
  public long id;
  public String name;
}

就是这样;虽然有匹配的ids,但也许没有必要尝试模拟这种依赖。