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

cop*_*lii 5 java json gson

我有一个类层次结构,我想要转换为GSON和从GSON转换.我不确定如何使用GSON(我目前有一个Factory查看JSONObject 的类,并且基于是否存在键,它调用正确的构造函数,然后将其部分工作委托给超类).当我将这些对象存储在本地SQLite DB中时,我使用整数来表示它们的类型,工厂类使用此类型来调用正确的构造函数.我在JSON中没有这种类型(这不是我的).

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

在下面的示例中,...在JSON括号内部处理,因为可能有或没有更多元素

这是类层次结构的细分:

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

有两个主要的抽象子类型:( TypeA有json键"a")和TypeB(有json键"b")

类型A

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

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

的TypeB

例: {"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" ...} ...}

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

我看TypeAdapterTypeAdapterFactory,但我仍然不知道如何处理这一点,因为我要看看传入的JSON的内容.

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

谢谢.

Jes*_*son 5

有一个TypeAdapter称为RuntimeTypeAdapterFactory的标准扩展名,使此操作变得简单。

测试用例提供了一些示例代码:

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);
Run Code Online (Sandbox Code Playgroud)


cop*_*lii 5

因此,RTTAF方法是朝着正确的方向发展,但它希望我有一个字段来表示我正在使用哪个子类型.在我的具体情况下,我有这些亚型的子类型和子类型.这就是我最终做的事情:

更新:创建Github要点

注意:在我的测试项目(下面)中,我使用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.");
            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

SuperType.java

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

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

TypeA.java

@Getter
@Setter
@EqualsAndHashCode (callSuper = true)
@ToString (callSuper = true)
public class TypeA extends SuperType {}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

其他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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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