我正在使用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/