使用 GSON 反序列化嵌套对象

Deserialize nested object with GSON

我正在尝试反序列化以下结构

{ meta: { keywords:  [a, b, c, d]}  ...  }

其他有效结构是

{ meta: { keywords: "a,b,c,d"}  ... }

{ meta: {keywords: "a"}  ...}

我有这个类

public class Data {
   @PropertyName("meta")
   MetaData meta;
   ...
}


public class  MetaData {
    List<String> keywords;
    ...
}

和自定义解串器

public static class CustomDeserilizer implements JsonDeserializer<MetaData>{
    @Override
    public MetaData deserialize(JsonElement json, Type typeOfT,  JsonDeserializationContext context) throws JsonParseException {
        List<String> keywords = null;
        Gson gson = new Gson();
        MetaData metaData = gson.fromJson(json, AppMetaData.class);
        JsonObject jsonObject = json.getAsJsonObject();

        if (jsonObject.has("keywords")) {
            JsonElement elem = jsonObject.get("keywords");
            if (elem != null && !elem.isJsonNull()) {

                if (jsonObject.get("keywords").isJsonArray()) {
                    keywords = gson.fromJson(jsonObject.get("keywords"),   new TypeToken<List<String>>() {
                    }.getType());
                } else {
                    String keywordString = gson.fromJson(jsonObject.get("keywords"), String.class);
                    keywords = new ArrayList<String>();
                    list.addAll(Arrays.asList(keywordString.split(",")));
                }
            }
        }
       metaData.setKeywords(keywords);
}

然后我尝试应用反序列化器:

Gson gson = new GsonBuilder()              
        .registerTypeAdapter(Data.class,new CustomDeserilizer())               
        .create();

但是我遇到了一个解析错误,因为我正在尝试反序列化数据而不是元数据,我该如何应用这个反序列化器才能使其正常工作?

首先,将您的 class 重命名为元数据而不是元数据,并将关键字字符串而不是 List.Then 使用以下内容将您的 JSonString 映射到您的对象中。

Gson gson = new GsonBuilder().create();
Meta meta = gson.from(yourJsonString,Meta.class);

为了只获取关键词,你需要这个。

JsonObject jsonObject = new JsonObject(yourJSonString);
String data = jsonObject.getJsonObject("meta").getString("keywords");

keywords is a JsonObject not an JsonArray so you can't directly map it onto List. You can split the string to get keywords in an array.

String keywords[] = data.split(",");

我解决了为我的 class 数据创建解串器的问题。

public static class DataDeserilizer implements JsonDeserializer {
    @Override
    public Data deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

        Gson gson = new Gson();
        Data data = gson.fromJson(json, Data.class);
        JsonObject jsonObject = json.getAsJsonObject();
        if (jsonObject.has("meta")) {
            JsonElement elem = jsonObject.get("meta");
            if (elem != null && !elem.isJsonNull()) {

                Gson gsonDeserializer = new GsonBuilder()
                        .registerTypeAdapter(MetaData.class, new CustomDeserilizer())
                        .create();
                gsonDeserializer.fromJson(jsonObject.get("meta"), Data.class);
            }
        }

        return data;
    }



}

Gson gson = new GsonBuilder()              
    .registerTypeAdapter(Data.class,new DataDeserilizer())               
    .create();

很明显,但是有更优雅的解决方案吗?

这是一个简洁的解决方案,它利用 Java 继承来表示嵌套结构;因此不需要提供任何实际的实例成员字段(映射等)来捕获 GSON 映射的嵌套 String 数据。

第 1 步:为了可读性,创建一个空对象来表示嵌套映射

public class StateRegionCitiesMap extends HashMap<String, List<String>> {

}

第二步:添加一行实际代码来做映射; 没有其他 serialize/deserialize 逻辑需要管理

protected void loadContent(JsonObject stateRegionsJsonObject) {

    HashMap<String, StateRegionCitiesMap> stateRegionCitiesMap =
            mGson.fromJson(
                    stateRegionsJsonObject,
                    new TypeToken<HashMap<String, StateRegionCitiesMap>>() {
                    }.getType()
            );
}

或者,您可以完全跳过包装器 class,直接将 <String, List<String>> 放入 GSON 调用中。但是,我发现一个明确的对象对 inform/remind 阅读代码的人有帮助,目的是什么。


示例JSON:

classStateRegionCitiesMap表示多层映射结构,例如:

[US State] -> [State-Region Key] -> [Sub-Region Key] -> CitiesArray[]

"CA": {
  "Central CA": {
    "Central Valley": [
      "FRESNO",
      "VISALIA"
    ],
    "Sacramento Area": [
      "SACRAMENTO",
      "EL DORADO HILLS"
    ]
  },