【问题标题】:Build an instance of Gson that can inflate subclasses构建一个可以扩展子类的 Gson 实例
【发布时间】:2017-04-01 17:07:58
【问题描述】:

我有一个对象列表,我需要将它们反序列化为适当的子类。我已经能够通过构建如下所示的 gson 对象来完成这项工作

    JsonDeserializer<Shape> jdTypes = (json, typeOfT, jsonContext) -> {
        JsonObject jsonObject = json.getAsJsonObject();
        int type = jsonObject.get("type").getAsInt();

        Gson gson = new Gson();

        switch (type){
            case Types.TYPE_CARD:
                return gson.fromJson(json, Card.class);

            case Types.TYPE_BLOCK:
                return gson.fromJson(json, Block.class);

            case Types.TYPE_STRIPE:
                return gson.fromJson(json, Stripe.class);

            //...

        }
    };

    GsonBuilder builder = new GsonBuilder();
    builder.registerTypeAdapter(Shape.class, jdTypes);
    builder.create();

虽然它有效,但必须有更好的方法来做到这一点。目前我正在我的 GsonBuilder 中创建一个 Gson 实例。哎呀。有没有办法可以使用我即将构建的 Gson 对象来反序列化不同的子类型?

【问题讨论】:

    标签: android json inheritance serialization gson


    【解决方案1】:

    如果我对这个问题的理解正确,您可以直接使用JsonDeserializationContext

    return jsonContext.deserialize(json, classOfType(type));
    

    classOfType() 应该只返回与您的形状的type 对应的类。

    【讨论】:

      【解决方案2】:

      无需重新发明轮子。您的问题在 Gson 范围内是经典的,并且有一个特殊的 RuntimeTypeAdapterFactory 旨在解决此类问题。例如,如果你有这样的事情:

      final class Types {
      
          private Types() {
          }
      
          static final int TYPE_CARD = 1;
          static final int TYPE_BLOCK = 2;
          static final int TYPE_STRIPE = 3;
      
          abstract static class Abstract {
      
              private Abstract() { }
      
              static final class Card extends Abstract { }    
              static final class Block extends Abstract { }   
              static final class Stripe extends Abstract {}
      
          }
      
      }
      

      您可以配置和使用RuntimeTypeAdapterFactory:

      private static final Gson gson = new GsonBuilder()
              .registerTypeAdapterFactory(
                      RuntimeTypeAdapterFactory.of(Types.Abstract.class, "type")
                              .registerSubtype(Types.Abstract.Card.class, String.valueOf(Types.TYPE_CARD))
                              .registerSubtype(Types.Abstract.Block.class, String.valueOf(Types.TYPE_BLOCK))
                              .registerSubtype(Types.Abstract.Stripe.class, String.valueOf(Types.TYPE_STRIPE))
              )
              .create();
      

      那么你甚至不需要其他任何东西:

      private static final Type abstractListType = new TypeToken<List<Types.Abstract>>() {
      }.getType();
      
      public static void main(final String... args) {
          gson.<List<Types.Abstract>>fromJson("[{\"type\":1},{\"type\":2},{\"type\":3}]", abstractListType)
                  .stream()
                  .map(Types.Abstract::getClass)
                  .forEach(System.out::println);
      }
      

      输出:

      类 q43159721.Types$Abstract$Card
      类 q43159721.Types$Abstract$Block
      类 q43159721.Types$Abstract$Stripe

      【讨论】:

      • 知道为什么 RuntimeTypeAdapterFactory 不是定义类型吗?
      • @Canoe RuntimeTypeAdapterFactory 是 Gson extras 的一部分,而不是主库,因此没有发布到 Maven Central。复制/粘贴或注册如下内容:mvnrepository.com/artifact/org.danilopianini/gson-extras/0.1.0
      • 谢谢我想我可能是一个额外的包,但我正在寻找一个由谷歌创作的包。看起来这是第三方扩展。谢谢。s
      • @Canoe 好吧,如果它是官方 Gson 源代码存储库的一部分,它本身就不是第 3 方扩展。 :) 老实说,我不确定将原始源代码包含到您的代码库中是否合法(我真的害怕任何版权标题哈哈)。但是,是的,添加像 org.danilopianini:gson-extras:0.1.0 这样的第三方库绝对是优先级最低的选择。
      猜你喜欢
      • 1970-01-01
      • 2015-11-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-24
      相关资源
      最近更新 更多