簡體   English   中英

JSON 在傳遞基類類型引用時僅序列化基 class 字段

[英]JSON serialize only base class fields when a base class-typed reference is passed

我正在使用Gson庫。 What is a clean / idiomatic way to ask Gson to serialize only the fields of the base class when the object give to Gson is of the base class type? 請注意,這與詢問如何始終排除特定字段的類似問題(例如this one )不同。 在我的用例中,我只希望在 Gson 庫通過派生的 class ZA8CFDE6331typedC4666 參考基類傳遞時排除繼承的 class 字段。 否則,即如果 Gson 庫通過派生類類型引用傳遞派生 class object ,那么我希望這些字段出現在序列化中。

SSCCE如下:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

class A {
    public int a;
    public A(final int a) {
        this.a = a;
    }
}

class B extends A {
    public int b;

    public B(final int a, final int b) {
        super(a);
        this.b = b;
    }

}

public class Main {

    public static void main(String args[]) {

        final A a = new B(42, 142);
        final Gson gson = new GsonBuilder().serializeNulls().create();

        System.out.printf("%s\n", gson.toJson(a));
    }
}

以上打印:

{"b":142,"a":42}

我正在尋找一種干凈的方式來打印它:

{“一”:42}

但是,如果使用以下代碼:

final B b = new B(42, 142);

...然后我希望gson.toJson(b)確實返回:

{"b":142,"a":42}

有沒有一種干凈的方法來實現這一目標?

更新

在撰寫本文時,公認的答案建議使用在這種情況下確實有效的toJson(o, A.class) 但是,這種方法似乎不能很好地擴展到 generics。 例如:

class A {
    public int a;
    public A(final int a) {
        this.a = a;
    }
}

class B extends A {
    public int b;

    public B(final int a, final int b) {
        super(a);
        this.b = b;
    }
}

class Holder<T> {
    public final T t;

    public Holder(final T t) {
        this.t = t;
    }
}

final A a = new B(42, 142);
final Holder<A> holder = new Holder<A>(a);
final Gson gson = new GsonBuilder().serializeNulls().create();

final Type TYPE= new TypeToken<Holder<A>>() {}.getType();
System.out.printf("%s\n", gson.toJson(holder, TYPE));

遺憾的是,上面的打印:

{"t":{"b":142,"a":42}}

Gson 用戶手冊中似乎沒有直接的配置。 但有一點值得一試:

您可以使用 Gson 序列化程序來使用動態ExclusionStrategy 在這里,“動態”策略意味着每次您需要序列化 Class Foo時,您都需要一個配置了OnlyFieldsFor<Foo> startegy 的 Gson 實例。

ExclusionStrategy是一個接口,它指定了 2 個方法:

  1. shouldSkipClass() - 使用它來過濾掉你不想要的類。

  2. shouldSkipField() - 這很有用。 它使用FieldAttributes調用,其中包含有關聲明字段的 class 的信息。如果未在基礎 class 中聲明字段,則返回 false。

有沒有一種干凈的方法來實現這一目標?

是的。

盡管排除策略足夠靈活,可以涵蓋這種情況,但您不需要排除策略。 數據包類的序列化和反序列化由ReflectiveTypeAdapterFactory中的TypeAdapter實現驅動。 這種類型的適配器考慮了最具體的 class 字段

話雖如此,您唯一需要的是指定最具體的類型來獲取字段:

final Object o = new B(42, 142);
System.out.println(gson.toJson(o, A.class));

它對您不起作用的原因是您的代碼隱含地指定了最具體的 class ,就像您使用gson.toJson(o, o.getClass())gson.toJson(o, B.class)對於上面的代碼。

泛型類型問題

恐怕ReflectiveTypeAdapterFactory在這種情況下無法考慮類型參數。 不確定getRuntimeTypeIfMoreSpecific方法究竟做了什么以及它的最初目的是什么,但它會將A類型“轉換”為具體的B類型,因為type instanceof Class<?>檢查允許使用type = value.getClass(); 覆蓋字段類型type = value.getClass(); . 看起來您必須針對這種情況實施一種解決方法:

.registerTypeAdapterFactory(new TypeAdapterFactory() {
    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
        final Class<? super T> rawType = typeToken.getRawType();
        if ( rawType != Holder.class ) {
            return null;
        }
        final Type valueType = ((ParameterizedType) typeToken.getType()).getActualTypeArguments()[0];
        @SuppressWarnings({"unchecked", "rawtypes"})
        final TypeAdapter<Object> valueTypeAdapter = (TypeAdapter) gson.getDelegateAdapter(this, TypeToken.get(valueType));
        final TypeAdapter<Holder<?>> typeAdapter = new TypeAdapter<Holder<?>>() {
            @Override
            public void write(final JsonWriter out, final Holder<?> value)
                    throws IOException {
                out.beginObject();
                out.name("t");
                valueTypeAdapter.write(out, value.t);
                out.endObject();
            }

            @Override
            public Holder<?> read(final JsonReader in)
                    throws IOException {
                Object t = null;
                in.beginObject();
                while ( in.hasNext() ) {
                    final String name = in.nextName();
                    switch ( name ) {
                    case "t":
                        t = valueTypeAdapter.read(in);
                        break;
                    default:
                        // do nothing;
                        break;
                    }
                }
                in.endObject();
                return new Holder<>(t);
            }
        }.nullSafe();
        @SuppressWarnings("unchecked")
        final TypeAdapter<T> castTypeAdapter = (TypeAdapter<T>) typeAdapter;
        return castTypeAdapter;
    }
})

不幸的是,在您的Gson實例中使用此類型適配器會使@SerializedName@JsonAdapter和其他很酷的東西不可用,並使其堅持硬編碼的字段名稱。 這可能是一個 Gson 錯誤。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM