繁体   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