使用GSON反序列化嵌套对象

8
我正在尝试对以下结构进行反序列化。
{ 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();

但是我遇到了解析错误,因为它试图反序列化Data而不是MetaData,我该如何应用这个反序列化器使其正常工作?

4个回答

11

我创建了一个针对我的Data类的反序列化器来解决它。

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

很显然,但是否有更优雅的解决方案?


0

这应该很容易实现你想要的功能。你应该定义一个内部静态类。你可以继续嵌套类来定义关键字,如class Keywords等。只需记得在包含类中有一个字段,即在你的内部类中有private Keywords keywords;

在你的主类中:

Gson gson = new Gson();
Data data = gson.fromJson(SOME_JSON_STRING, Data.class);

在一个名为Data的类中:
public class Data {
    private Meta meta;
    
    static class Meta{
        private String[] keywords;
    }
}

0
首先,将您的类从 metadata 重命名为 meta,并将关键字从 List 更改为 String。然后使用以下代码将您的 JSON 字符串映射到对象中。
Gson gson = new GsonBuilder().create();
Meta meta = gson.from(yourJsonString,Meta.class);

为了仅获取关键词,您需要这个。
JsonObject jsonObject = new JsonObject(yourJSonString);
String data = jsonObject.getJsonObject("meta").getString("keywords");

关键字是JsonObject而不是JsonArray,因此您不能直接将其映射到List。您可以将字符串拆分为数组以获取关键字。
String keywords[] = data.split(",");

嗨,Umer,我为我的类Data创建了反序列化器,解决了问题(为嵌套对象分配自定义反序列化器),但我正在寻找像Jackson中的@JsonDeserialize注释一样的解决方案。 - Osarez
1
String keywords[] = data.split(","); 是一个非常不好的,可怕的想法,你应该永远不要使用它。如果数据中有换行符或额外的空格会怎样呢?您应始终使用与JSON标准完全兼容的方法对数据进行解码。这种方法将无法解码(或更糟糕的是,错误地解码)有效的JSON数据。 - Peter Schorn

0

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

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

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

}

步骤2:添加一行实际的代码来进行映射;无需其他序列化/反序列化逻辑来管理

protected void loadContent(JsonObject stateRegionsJsonObject) {

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

或者,您可以完全跳过包装类,并直接在 GSON 调用中放置 <String,List<String>>。然而,我发现一个显式对象有助于告知/提醒阅读代码的人,其目的是什么。


示例JSON:

StateRegionCitiesMap类表示一个多层地图结构,例如:

[美国州] -> [州-地区键] -> [子地区键] -> CitiesArray[]

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

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