【问题标题】:How to write GSON custom deserializer for embedded object with two possible types如何为具有两种可能类型的嵌入式对象编写 GSON 自定义反序列化器
【发布时间】:2025-11-22 05:30:02
【问题描述】:

我在尝试反序列化 java 对象时遇到了麻烦,因为对象内的字段(“信息”)可以是两种可能的类型之一:ArrayList 或只是一个 String。这是我到目前为止所做的:

首先,创建类Base:

public class Base {
}

接下来创建子类:

public class GoodInfo extends Base {
    public ArrayList<MyCustomObject> info;
}

public class BadInfo extends Base {
    public String info;
}

所以现在我想解析我的 JSON,它是 Base 对象的 ArrayList(即对象的 ArrayList,其中每个对象都是 ArrayList 或字符串):

Type listOfBase = new TypeToken<ArrayList<Base>>(){}.getType();
ArrayList<Base> resp=gson.fromJson(jsonText, listOfBase);

我知道要让它工作,我必须编写一个自定义反序列化器。反序列化器如下所示:

private class MyCustomDeserializer implements JsonDeserializer<DateTime> {
    public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
  throws JsonParseException {
        // WHAT DO I DO HERE?
    }
}

如您所见,我不知道如何尝试反序列化每个子类型并返回有效的类型。有人知道怎么做吗?

我认为它看起来像这样:

private class MyCustomDeserializer implements JsonDeserializer<DateTime> {
    public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
  throws JsonParseException {
        try {
            GoodInfo goodInfo=SOMEHOW TRY TO DESERIALIZE json INTO A GoodInfo object
            return goodInfo;
        } catch {
            //
        }
        try {
            BadInfo badInfo=SOMEHOW TRY TO DESERIALIZE json INTO A BadInfo object
            return badInfo;
        } catch {
            throw new JsonParseException("Could not deserialize");
        }
    }
}

根据 GSON,我不能对传入的 json 使用 context.deserialize: 对指定对象调用默认反序列化。永远不应在作为 JsonDeserializer.deserialize(JsonElement, Type, JsonDeserializationContext) 方法的参数接收的元素上调用它。这样做将导致无限循环,因为 Gson 将再次调用自定义反序列化器。

那么我该怎么做呢?

【问题讨论】:

    标签: java json serialization gson


    【解决方案1】:

    不是documentation reads(强调我的):

    ...您永远不应该在 传递 json 的同一类型上调用它,因为这将导致无限循环...

    调用context.deserialize(...) 完全没问题,只要类型不同。

    您可以使用json 检查info 字段并根据元素类型采取适当的操作,而不是在反序列化器中捕获异常,例如:

    public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        final JsonElement elem = json.getAsJsonObject()
                                     .get("info");
        if (elem.isJsonArray()) {
            return context.deserialize(json, GoodInfo.class);
        }
        return context.deserialize(json, BadInfo.class);
    }
    

    或者,您可以通过修改超类来完全绕过自定义JsonDeserializer。拉起info 字段为Object,例如:

    public class Base {
        public Object info;
    }
    

    将允许 Gson 适当地反序列化该值。

    【讨论】:

      【解决方案2】:
      class MyCustomDeserializer implements JsonDeserializer<Base>
      {
          public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
          {
              try
              {
                  return context.deserialize(json, GoodInfo.class);
              }
              catch (JsonParseException e)
              {
                  return context.deserialize(json, BadInfo.class);
              }
          }
      }
      

      此解决方案运行良好,因为 GoodInfoBadInfo 与 GSON 偏离路线的 Base 类型不同,因为从对象的角度来看,GoodInfoBadInfo 实例 Base 实例。

      考虑到两个类都实现了toString()

      class GoodInfo extends Base
      {
          public ArrayList<String> info;
      
          public String toString()
          {
              return info.toString();
          }
      }
      
      class BadInfo extends Base
      {
          public String info;
      
          public String toString()
          {
              return info;
          }
      }
      

      测试:

      Type listOfBase = new TypeToken<ArrayList<Base>>(){}.getType();
      GsonBuilder gsonBuilder = new GsonBuilder();
      gsonBuilder.registerTypeAdapter(Base.class, new MyCustomDeserializer());
      Gson gson = gsonBuilder.create();
      ArrayList<Base> resp = gson.fromJson("[{\"info\": \"test\"}, {\"info\": [\"test\", \"test\", \"test\"]}]", listOfBase);
      System.out.println(resp.size());
      for (Object o : resp) System.out.println(o);
      

      打印出来:

      2 test [test, test, test]

      【讨论】:

        最近更新 更多