一个对象中的变量需要自定义Gson反序列化器

34

我的问题示例:

我们有一个类型为Apple的对象。Apple有一些成员变量:

String appleName; // The apples name
String appleBrand; // The apples brand
List<Seed> seeds; // A list of seeds the apple has

种子对象如下所示。

String seedName; // The seeds name
long seedSize; // The size of the seed

现在当我获取到一个苹果对象时,这个苹果可能有一个或多个种子,也可能没有任何种子!

带有一个种子的示例JSON苹果:

{
"apple" : {
   "apple_name" : "Jimmy", 
   "apple_brand" : "Awesome Brand" , 
   "seeds" : {"seed_name":"Loopy" , "seed_size":"14" }
  }
}

一个带有两颗种子的苹果的JSON示例:

{
"apple" : {
   "apple_name" : "Jimmy" , 
   "apple_brand" : "Awesome Brand" , 
   "seeds" : [ 
      { 
         "seed_name" : "Loopy",
         "seed_size" : "14"
      },
      {
         "seed_name" : "Quake",
         "seed_size" : "26"
      } 
  ]}
}

问题在于第一个示例是种子的JSONObject,第二个示例是种子的JSONArray。现在我知道它是不一致的JSON,最简单的修复方式是修复JSON本身,但不幸的是,我从别人那里获取JSON,因此我无法修复它。有什么最简单的方法可以解决这个问题?


27
你从哪里找到苹果和种子的JSON API? :P - jjnguy
4个回答

42

您需要为Apple类型注册自定义类型适配器。在类型适配器中,您将添加逻辑以确定是否给出了数组或单个对象。使用该信息,您可以创建您的Apple对象。

除了以下代码之外,还要修改您的Apple模型对象,以便不自动解析seeds字段。将变量声明更改为类似以下内容:

private List<Seed> seeds_funkyName;

以下是代码:

GsonBuilder b = new GsonBuilder();
b.registerTypeAdapter(Apple.class, new JsonDeserializer<Apple>() {
    @Override
    public Apple deserialize(JsonElement arg0, Type arg1,
        JsonDeserializationContext arg2) throws JsonParseException {
        JsonObject appleObj = arg0.getAsJsonObject();
        Gson g = new Gson();
        // Construct an apple (this shouldn't try to parse the seeds stuff
        Apple a = g.fromJson(arg0, Apple.class);
        List<Seed> seeds = null;
        // Check to see if we were given a list or a single seed
        if (appleObj.get("seeds").isJsonArray()) {
            // if it's a list, just parse that from the JSON
            seeds = g.fromJson(appleObj.get("seeds"),
                    new TypeToken<List<Seed>>() {
                    }.getType());
        } else {
            // otherwise, parse the single seed,
            // and add it to the list
            Seed single = g.fromJson(appleObj.get("seeds"), Seed.class);
            seeds = new ArrayList<Seed>();
            seeds.add(single);
        }
        // set the correct seed list
        a.setSeeds(seeds);
        return a;
    }
});

For some more info, see the Gson guide.


我遇到了同样的问题,我尝试了上面的代码,但是在这一行中出现了异常 "// Construct an apple (this shouldn't try to parse the seeds stuff Apple a = g.fromJson(arg0, Apple.class);",错误是“预期BEGIN_ARRAY但实际得到的是BEGIN_OBJECT”,你能帮我解决这个问题吗? - Sam
非常好的答案,但我遇到了与@Sam相同的错误。我通过创建一个new() Apple对象,设置seeds,然后设置其他字段来规避了这个问题。 - sschrass
@SatelliteSD,你能分享一下你的代码片段吗? - letroll
1
这节省了我数小时的工作时间。如果可以的话,我会给予+10票 :) - parohy
2
不要在适配器内部初始化Gson!使用arg2.fromJson(…)代替! - Denis Nek

2

我也遇到了同样的问题。我认为我的解决方案略微更简单和更通用:

Gson gson = new GsonBuilder()
        .registerTypeAdapter(List.class, new JsonSerializer<List<?>>() {
            @Override
            public JsonElement serialize(List<?> list, Type t,
                    JsonSerializationContext jsc) {
                if (list.size() == 1) {
                    // Don't put single element lists in a json array
                    return new Gson().toJsonTree(list.get(0));
                } else {
                    return new Gson().toJsonTree(list);
                }
            }
        }).create();

当然,我同意原帖的观点,最好的解决方案是更改json。一个大小为1的数组并没有什么问题,并且可以使序列化和反序列化变得更加简单!不幸的是,有时这些更改超出您的控制范围。

不知道是谁给这篇帖子点了1分,但问题是关于反序列化而这是一个序列化器。 - Oliver Hausler

1

1
请注意,"["是声明数组的符号。现在请注意我的一个种子的示例中没有"["。 - Jacob Nelson
我的意思是用Seed[] seeds替换List<Seed> seeds应该会有所帮助。 - Dmitry Negoda
尝试过了。它给我抛出了这个异常:“com.google.gson.JsonObject 无法转换为 com.google.gson.JsonArray”。 - Jacob Nelson

0

如果您无法更改JSON(因为您从他人那里获取它),则最简单的解决方案是在Java类中更改您的Apple和Seed类变量名称,以便与解析的JSON匹配。

更改为:

Apple Class
-----------
String apple_name; // The apples name
String apple_brand; // The apples brand
List<Seed> seeds; // A list of seeds the apple has
And the seed object looks as follows.

Seed Class
-----------
String seed_name; // The seeds name
long seed_size; // The size of the seed

仔细查看JSON,在第一种情况下它只是一个对象,而在第二种情况下它是一个数组。因此,这并不能解决问题。您必须像接受的答案中那样注册您的“typeAdapter”。 - Farid

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