我正在使用Gson库。当提供给Gson的对象是基类类型时,有什么干净/惯用的方式要求Gson仅序列化基类的字段?请注意,这与类似的问题(例如this one)不同,后者询问如何始终排除特定字段。在我的用例中,我只希望在Gson库通过基类类型的引用传递派生类对象时排除继承的类字段。否则,即如果Gson库通过派生类类型的引用传递了派生类对象,那么我希望这些字段出现在序列化中。

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}


我正在寻找一种干净的方法来打印它:


  {“ a”:42}


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

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


...然后我希望gson.toJson(b)确实返回:


  {“ b”:142,“ a”:42}


有没有一种干净的方法可以做到这一点?

更新

accepted answer at the time of this writing建议使用在这种情况下有效的toJson(o, A.class)。但是,此方法似乎不适用于泛型。例如。:

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}}

最佳答案

有没有一种干净的方法可以做到这一点?


是。

尽管排除策略具有足够的灵活性来涵盖这种情况,但您并不需要排除策略。数据包类的序列化和反序列化由TypeAdapter中的ReflectiveTypeAdapterFactory实现驱动。此类型的适配器将the most concrete class fields考虑在内。

话虽如此,您唯一需要的就是specifying从以下字段中获取字段的最具体的类型:

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


之所以对您不起作用,是因为您的代码指定了最具体的类implicitly,就像您对上述代码使用gson.toJson(o, o.getClass())gson.toJson(o, B.class)一样。

泛型类型问题

恐怕ReflectiveTypeAdapterFactory在这种情况下不能考虑类型参数。
不确定getRuntimeTypeIfMoreSpecific方法到底能做什么以及它的初衷是什么,但是由于A检查允许使用overwrite字段类型,因此将B类型“转换”为具体的type instanceof Class<?>类型。 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错误。

关于java - 传递基类类型的引用时,JSON仅序列化基类字段,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/61289314/

10-11 22:32
查看更多