【问题标题】:How do I parse class hierarchies with gson?如何使用 gson 解析类层次结构?
【发布时间】:2014-02-08 02:09:00
【问题描述】:

我有一个要与 GSON 相互转换的类层次结构。我不确定如何使用 GSON 来解决这个问题(我目前有一个 Factory 类,它查看 JSONObject 并根据是否存在键调用正确的构造函数,这反过来又将它的一些工作委托给超级班级)。当我将这些对象存储在本地 SQLite DB 中时,我使用一个整数来表示它们的类型,工厂类使用这种类型来调用正确的构造函数。我在 JSON 中没有这个 type(这不是我的)。

如何根据 JSON 对象的内容告诉 GSON 要为我实例化哪种类型的对象?

在下面的示例中,将 JSON 括号内的 ... 视为 可能有也可能没有更多元素

以下是类层次结构的细分:

有一个基本抽象类型:SuperType,带有 JSON 表示 {"ct":12345,"id":"abc123 ...}

主要有 2 种抽象子类型:TypeA(具有 json 键 "a")和 TypeB(具有 json 键 "b"

A型

示例:{"ct":12345,"id":"abc123, "a":{...}}

TypeA 有 15 个孩子(我们将这些 TypeA_A 称为 TypeA_P)。这些对象的 JSON 表示形式类似于 {"ct":12345,"id":"abc123, "a":{"aa":1 ...} ...}{"ct":12345,"id":"abc123, "a":{"ag":"Yo dawg I head you like JSON" ...} ...}

B型

示例:{"ct":12345,"id":"abc123, "b":{...} ...}

TypeB 有另一个抽象子类型 (TypeB_A) 和几个子类型(我们将它们称为 TypeB_BTypeB_I)。这些对象的 JSON 表示将是 {"ct":12345,"id":"abc123, "b":{"ba":{...} ...} ...}{"ct":12345,"id":"abc123, "b":{"bg":"Stayin alive" ...} ...}

可以把它全部放在一个怪物类型中,并将每个子类型视为一个内部对象,但我最终会得到很多为空的内部成员(有点像一棵树,有很多枝条,无处可去)。因此,我最终会得到很多if (something==null),只是为了确定我正在处理的这些类型中的哪一种。

我查看了TypeAdapterTypeAdapterFactory,但我仍然不确定如何处理这个问题,因为我必须查看传入 JSON 的内容。

如何根据 JSON 对象的内容告诉 GSON 要为我实例化哪种类型的对象?

谢谢。

【问题讨论】:

    标签: java json gson


    【解决方案1】:

    有一个名为RuntimeTypeAdapterFactory 的标准扩展TypeAdapter 让这变得简单。

    test case 提供了一些示例代码:

    RuntimeTypeAdapterFactory<BillingInstrument> rta = RuntimeTypeAdapterFactory.of(
        BillingInstrument.class)
        .registerSubtype(CreditCard.class);
    Gson gson = new GsonBuilder()
        .registerTypeAdapterFactory(rta)
        .create();
    
    CreditCard original = new CreditCard("Jesse", 234);
    assertEquals("{\"type\":\"CreditCard\",\"cvv\":234,\"ownerName\":\"Jesse\"}",
        gson.toJson(original, BillingInstrument.class));
    BillingInstrument deserialized = gson.fromJson(
        "{type:'CreditCard',cvv:234,ownerName:'Jesse'}", BillingInstrument.class);
    assertEquals("Jesse", deserialized.ownerName);
    assertTrue(deserialized instanceof CreditCard);
    

    【讨论】:

    • 我也成功使用过这个东西。为它添加一个默认处理程序作为任何无法识别的子类的包罗万象也是相当容易的,尽管你应该小心。
    • @Jesse 谢谢。我确实遇到了 RTTAF,但我仍然不确定如何确定 which 类......还是 GSON 会自动搜索 @SerializedName 字段以匹配它?我想我真正要问的是我是否必须手动查看 json 并给出 GSON 提示,还是它会根据注册的子类型自动计算出来?另外,我必须注册抽象类型还是只注册具体类型?我认为只注册具体的类就可以了,但问一下也无妨:)
    • @copolii 默认情况下,它会查找名为 typeString 字段以用作地图查找,但我相信您可以根据需要指定其他字段。
    • @Argyle 是的,谢谢,刚才在代码中注意到了这一点。会写一个小例子并测试一下。如果我能看到如何它是如何损坏的,那么它会更容易适应我的使用
    【解决方案2】:

    所以,RTTAF 方法是朝着正确的方向推进,但它希望我有一个字段来指示我正在使用的子类型。在我的具体情况下,我有这些子类型的子类型和子类型。这就是我最终要做的:

    更新Created Github gist

    注意:在我的测试项目(如下)中,我使用 GSON 和 Lombok 进行注释。

    类型工厂

    public class CustomTypeAdapterFactory implements TypeAdapterFactory {
        @Override
        public <T> TypeAdapter<T> create (final Gson gson, final TypeToken<T> type) {
            if (type.getRawType () != SuperType.class)
                return null;
    
            final TypeAdapter<T> delegate = gson.getDelegateAdapter (this, type);
    
            return new TypeAdapter<T> () {
                @Override
                public void write (final JsonWriter jsonWriter, final T t) throws IOException {
                    delegate.write (jsonWriter, t);
                }
    
                @Override
                public T read (final JsonReader jsonReader) throws IOException, JsonParseException {
                    JsonElement tree = Streams.parse (jsonReader);
                    JsonObject object = tree.getAsJsonObject ();
    
                    if (object.has ("a"))
                        return (T) readTypeA (tree, object.getAsJsonObject ("a"));
    
                    if (object.has ("b"))
                        return (T) readTypeB (tree, object.getAsJsonObject ("b"));
    
                    throw new JsonParseException ("Cannot deserialize " + type + ". It is not a valid SuperType JSON.");
                }
    
                private TypeA readTypeA (final JsonElement tree, final JsonObject a) {
                    if (a.has ("aa"))
                        return gson.getDelegateAdapter (CustomTypeAdapterFactory.this, TypeToken.get (TypeA_A.class)).fromJsonTree (tree);
    
                    if (a.has ("ab"))
                        return gson.getDelegateAdapter (CustomTypeAdapterFactory.this, TypeToken.get (TypeA_B.class)).fromJsonTree (tree);
    
                    if (a.has ("ac"))
                        return gson.getDelegateAdapter (CustomTypeAdapterFactory.this, TypeToken.get (TypeA_C.class)).fromJsonTree (tree);
    
                    throw new JsonParseException ("Cannot deserialize " + type + ". It is not a valid TypeA JSON.");
                }
    
                private TypeB readTypeB (final JsonElement tree, final JsonObject b) {
                    if (b.has ("ba"))
                        return gson.getDelegateAdapter (CustomTypeAdapterFactory.this, TypeToken.get (TypeB_A.class)).fromJsonTree (tree);
    
                    if (b.has ("bb"))
                        return gson.getDelegateAdapter (CustomTypeAdapterFactory.this, TypeToken.get (TypeB_B.class)).fromJsonTree (tree);
    
                    if (b.has ("bc"))
                        return gson.getDelegateAdapter (CustomTypeAdapterFactory.this, TypeToken.get (TypeB_C.class)).fromJsonTree (tree);
    
                    throw new JsonParseException ("Cannot deserialize " + type + ". It is not a valid TypeB JSON.");
                }
            };
        }
    }
    

    SuperType.java

    @Getter
    @Setter
    @EqualsAndHashCode
    @ToString
    public class SuperType {
        @SerializedName ("ct")
        protected long creationTime;
        @SerializedName ("id")
        protected String id;
    }
    

    Type_A 在其级别中没有额外的数据,但确实有一些常见的行为(为了简单起见,这里省略了方法,因为它们与解析无关)。

    TypeA.java

    @Getter
    @Setter
    @EqualsAndHashCode (callSuper = true)
    @ToString (callSuper = true)
    public class TypeA extends SuperType {}
    

    TypeA_A.java

    @Getter
    @Setter
    @EqualsAndHashCode (callSuper = true)
    @ToString(callSuper = true)
    public class TypeA_A
      extends TypeA {
    
        @SerializedName ("a")
        protected AA aValue;
    
        @ToString
        private static class AA {
            @SerializedName ("aa")
            private String aaValue;
        }
    }
    

    其他 Type_A 子代与 TypeA_A 非常相似。

    Type_B 稍微复杂一些,因为它有自己的数据行为(再次为简单起见,省略了):

    TypeB.java

    @Getter
    @Setter
    @EqualsAndHashCode (callSuper = true)
    @ToString (callSuper = true)
    public class TypeB extends SuperType  {
    
    // no member declared here
    
        protected static abstract class B {
            @SerializedName ("b1")
            protected String b1Value;
            @SerializedName ("b2")
            protected String b2Value;
        }
    }
    

    Type_BA.java

    @Getter
    @Setter
    @EqualsAndHashCode (callSuper = true)
    @ToString (callSuper = true)
    public class TypeB_A
      extends TypeB {
    
        @SerializedName ("b")
        protected BA bValue;
    
        @ToString
        private static class BA extends B {
            @SerializedName ("ba")
            private String baValue;
        }
    }
    

    TypeB_B.java

    @Getter
    @Setter
    @EqualsAndHashCode (callSuper = true)
    @ToString (callSuper = true)
    public class TypeB_B
      extends TypeB {
    
        @SerializedName ("b")
        protected BB bValue;
    
        @ToString
        private static class BB extends B {
            @SerializedName ("bb")
            private String bbValue;
    
            @SerializedName ("bb1")
            private String bb1Value;
        }
    }
    

    这里可能有一些拼写错误,因为我必须更改实际的类型名称和值,但我将创建一个基本的 java 代码示例并将发布到 Github。

    感谢@Jesse Wilson 和@Argyle 帮助我指明了正确的方向。

    【讨论】:

      猜你喜欢
      • 2013-03-18
      • 2014-09-28
      • 2014-03-18
      • 1970-01-01
      • 1970-01-01
      • 2012-08-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多