简体   繁体   English

如何使用gson解析类层次结构?

[英]How do I parse class hierarchies with gson?

I have a class hierarchy that I want to convert to and from GSON. 我有一个类层次结构,我想要转换为GSON和从GSON转换。 I'm not sure how to approach this with GSON (I currently have a Factory class that looks at the JSONObject and based on presence or absence of keys it calls the right constructor, which in turn delegates some of its work to the super class). 我不知道如何使用GSON来处理这个问题(我目前有一个查看JSONObject的Factory类,它基于是否存在键调用正确的构造函数,而后者又将其部分工作委托给超类) 。 When I store these objects in the local SQLite DB, I use an integer to denote their type and the factory class uses this type to call the right constructor. 当我将这些对象存储在本地SQLite DB中时,我使用一个整数来表示它们的类型,工厂类使用这种类型来调用正确的构造函数。 I don't have this type in the JSON (which isn't mine). 我在JSON中没有这种类型 (这不是我的)。

How do I tell GSON based on the contents of the JSON object which type of object to instantiate for me? 如何根据JSON对象的内容告诉GSON要为我实例化哪种对象?

In the examples below, treat ... inside the JSON brackets as there may or may not be more elements 在下面的示例中,在JSON括号内处理...因为可能有或没有更多元素

Here's a breakdown of the class hierarchy: 这是类层次结构的细分:

There is a base abstract type: SuperType with a JSON representation {"ct":12345,"id":"abc123 ...} 有一个基本抽象类型:带有JSON表示的SuperType {"ct":12345,"id":"abc123 ...}

There are 2 main abstract sub types: TypeA (has json key "a" ) and TypeB (has json key "b" ) 有两个主要的抽象子类型: TypeA (具有json键“a” )和TypeB (具有json键“b”

TypeA 类型A

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

TypeA has 15 children (Let's call these TypeA_A to TypeA_P ). TypeA有15个孩子(我们将这些TypeA_A TypeA_P )。 The JSON representation of these objects would be something like {"ct":12345,"id":"abc123, "a":{"aa":1 ...} ...} or {"ct":12345,"id":"abc123, "a":{"ag":"Yo dawg I head you like JSON" ...} ...} 这些对象的JSON表示类似于{"ct":12345,"id":"abc123, "a":{"aa":1 ...} ...}{"ct":12345,"id":"abc123, "a":{"ag":"Yo dawg I head you like JSON" ...} ...}

TypeB 的TypeB

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

TypeB has another abstract subtype ( TypeB_A ) and few children (Let's call these TypeB_B to TypeB_I ). TypeB有另一个抽象子类型( TypeB_A )和几个子TypeB_A (让我们将这些TypeB_B TypeB_I )。 The JSON representation of these objects would be {"ct":12345,"id":"abc123, "b":{"ba":{...} ...} ...} or {"ct":12345,"id":"abc123, "b":{"bg":"Stayin alive" ...} ...} 这些对象的JSON表示形式为{"ct":12345,"id":"abc123, "b":{"ba":{...} ...} ...}{"ct":12345,"id":"abc123, "b":{"bg":"Stayin alive" ...} ...}

I could throw it all in one monster type and treat each of the sub types as an inner object, but I'll end up with a lot of inner members that are null (sort of like a tree with a lot of branches that lead to nowhere). 可以把它全部放在一个怪物类型中并将每个子类型视为一个内部对象,但我最终会得到很多内部成员为null(有点像一棵树,有很多分支导致无处)。 As a result, I'll end up with a lot of if (something==null) just to determine which one of these types I'm dealing with. 结果,我最终会得到很多if (something==null)只是为了确定我正在处理的这些类型中的哪一种。

I've looked at TypeAdapter and TypeAdapterFactory , but I'm still not sure how to approach this since I have to look at the content of the incoming JSON. 我已经查看了TypeAdapterTypeAdapterFactory ,但我仍然不确定如何处理这个问题,因为我必须查看传入JSON的内容。

How do I tell GSON based on the contents of the JSON object which type of object to instantiate for me? 如何根据JSON对象的内容告诉GSON要为我实例化哪种对象?

Thanks. 谢谢。

So, the RTTAF approach was a push in the right direction, but it expects me to have a field that denotes which subtype I'm using. 因此,RTTAF方法是朝着正确的方向发展,但它希望我有一个字段来表示我正在使用哪个子类型。 In my specific case, I have subtypes and subtypes of those subtypes. 在我的具体情况下,我有这些亚型的子类型和子类型。 This is what I ended up doing: 这就是我最终做的事情:

UPDATE : Created Github gist 更新创建Github要点

Note: In my test project (below), I'm using GSON and Lombok for annotations. 注意:在我的测试项目(下面)中,我使用GSON和Lombok进行注释。

The Type Factory 类型工厂

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 SuperType.java

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

Type_A has no additional data in its level, but does have some common behaviours (methods omitted here for simplicity and because they are irrelevant to parsing). Type_A在其级别中没有其他数据,但确实有一些常见的行为(为简单起见,这里省略了方法,因为它们与解析无关)。

TypeA.java TypeA.java

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

TypeA_A.java 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;
    }
}

Other Type_A children are very similar to TypeA_A. 其他Type_A儿童与TypeA_A非常相似。

Type_B is slightly more complex as it has its own data and behaviours (again, omitted for simplicity): Type_B稍微复杂一些,因为它有自己的数据行为(为简单起见,再次省略):

TypeB.java 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 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 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;
    }
}

There may be some typos here because I had to change the actual type names and values, but I will create a basic java code example and will post to Github. 这里可能存在一些拼写错误,因为我必须更改实际的类型名称和值,但我将创建一个基本的Java代码示例并将发布到Github。

Thanks to @Jesse Wilson and @Argyle for their help in pointing me in the right direction. 感谢@Jesse Wilson和@Argyle帮助我指明正确的方向。

There's a standard extension TypeAdapter called RuntimeTypeAdapterFactory that makes this straightforward. 有一个名为RuntimeTypeAdapterFactory的标准扩展TypeAdapter使这很简单。

The test case provides some sample code: 测试用例提供了一些示例代码:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM