如何使用Gson解码具有未知字段的JSON?

37

我有类似于这样的JSON:

{
  "unknown_field": {
    "field1": "str",
    "field2": "str",
    "field3": "str",
    "field4": "str",
    "field5": "str"
  }, ......
}

我创建了类来映射这个 JSON。

public class MyModel implements Serializable {
  private int id;
  private HashMap<String, Model1> models;

  // getters and setter for id and models here
}

类Model1非常简单,只包含字符串字段。

但它不起作用。

编辑:这是JSON格式的样子:

{
    "1145": {
        "cities_id": "1145",
        "city": "Nawanshahr",
        "city_path": "nawanshahr",
        "region_id": "53",
        "region_district_id": "381",
        "country_id": "0",
        "million": "0",
        "population": null,
        "region_name": "Punjab"
    },
    "1148": {
        "cities_id": "1148",
        "city": "Nimbahera",
        "city_path": "nimbahera",
        "region_id": "54",
        "region_district_id": "528",
        "country_id": "0",
        "million": "0",
        "population": null,
        "region_name": "Rajasthan"
    }, 
    ...
}

3
将其解析为一个 Map。 - Hot Licks
我已经尝试过了,但没有结果。这个方法 - gson.fromJson(json, MyModel.class) 返回 null。 - NazarK
“unknown_field”是指JSON字符串中此对象的名称可能会变化吗? - Jonik
因为你没有将它解析成一个Map。Gson必须有一个普通的解析器模式,可以生成一个Map或List,而不需要一个模型类。 - Hot Licks
1个回答

78

在 OP 评论了 JSON 的实际样式 后,我完全更新了答案。

Gson 2.0+ 的解决方案

刚刚学到,使用较新版本的 Gson 非常简单:

GsonBuilder builder = new GsonBuilder();
Object o = builder.create().fromJson(json, Object.class);

创建的对象是Map(com.google.gson.internal.LinkedTreeMap),如果您打印它,它看起来像这样:
{1145={cities_id=1145, city=Nawanshahr, city_path=nawanshahr, region_id=53, region_district_id=381, country_id=0, million=0, population=null, region_name=Punjab}, 
 1148={cities_id=1148, city=Nimbahera, city_path=nimbahera, region_id=54, region_district_id=528, country_id=0, million=0, population=null, region_name=Rajasthan}
...

使用自定义反序列化器的解决方案

(NB: 除非你被困在Gson 2.0之前的版本中,否则实际上你不需要自定义反序列化器。但是,了解如何在Gson中进行自定义反序列化(和序列化)仍然很有用,并且根据您要使用解析数据的方式,这通常可能是最佳方法。)

因此,我们确实正在处理随机/变化的字段名称。(当然,这种JSON格式不是很好;这种数据应该在一个 JSON数组中,这样它可以很容易地读入到List中。嗯,我们仍然可以解析这个。)

首先,这是我如何在Java对象中建模JSON数据:

// info for individual city
public class City    {
    String citiesId;
    String city;
    String regionName;
    // and so on
}

// top-level object, containing info for lots of cities
public class CityList  {
    List<City> cities;

    public CityList(List<City> cities) {
        this.cities = cities;
    }
}

接下来是解析。处理这种JSON的一种方法是为顶层对象(CityList)创建一个自定义反序列化器。

类似于这样:

public class CityListDeserializer implements JsonDeserializer<CityList> {

    @Override
    public CityList deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException {
        JsonObject jsonObject = element.getAsJsonObject();
        List<City> cities = new ArrayList<City>();
        for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
            // For individual City objects, we can use default deserialisation:
            City city = context.deserialize(entry.getValue(), City.class); 
            cities.add(city);
        }
        return new CityList(cities);
    }

}

一个需要注意的关键点是调用 jsonObject.entrySet(),它返回所有顶层字段(名称为“1145”,“1148”等)。这个Stack Overflow答案帮助我解决了这个问题。
完整的解析代码如下。请注意,您需要使用registerTypeAdapter()来注册自定义序列化器。
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(CityList.class, new CityListDeserializer());
Gson gson = builder.setFieldNamingPolicy(LOWER_CASE_WITH_UNDERSCORES).create();
CityList list = gson.fromJson(json, CityList.class);

这是一个完整的可执行示例,我用它来进行测试。除了Gson外,它还使用了Guava库。


@Nazik:好的,那么这就有点棘手了,但可以使用自定义反序列化器来解决。请查看我的编辑答案。 - Jonik
我再次更新了答案,因为最新的Gson版本中这非常简单。很高兴学到了关于Gson的新知识。 - Jonik
非常感谢您的帮助性回复。 - NazarK
感谢 LinkedTreeMap。手动解析 JSON 对象中的字符串值很困难,但至少它有所帮助。 - CoolMind

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接