【问题标题】:Can't properly serialize POJO with Iterable fields using Gson无法使用 Gson 正确序列化具有可迭代字段的 POJO
【发布时间】:2018-01-18 06:39:35
【问题描述】:

这是我正在尝试序列化的 POJO:

public class Bar {
    private final Foo foo;

    private final Iterable<String> list;

    private final Iterable<Map<String, String>> listOfMaps;
}

我是这样称呼它的

Bar bar = new Bar();
Foo foo = new Foo();
foo.field1 = "val1";
foo.field2 = "val2";
bar.foo = foo;
bar.list = ImmutableList.<String>of("fooList");
bar.listOfMaps = ImmutableList.<Map<String,String>>of(
                    ImmutableMap.<String,String>of("key", "val")
                );
new Gson().toJson(bar);

这是结果

{"foo":{"field1":"val1","field2":"val2"},"list":{},"listOfMaps":{}}

如您所见,POJO 序列化很好,但可迭代(番石榴集合的实例)没有正确序列化为 JSON。当我自己序列化这些字段时,它们显示得很好,但是当它们是 Bar 的字段时它不会正确序列化

例子:

new Gson().toJson(bar.list);

["fooList"]

【问题讨论】:

  • 既然你的Bar的字段是final的,为什么你可以在创建后设置值?

标签: java json gson guava java-6


【解决方案1】:

对于 Gson 来说,这似乎是一个老问题,这就是它最初的设计方式。 我有一些假设为什么它可能会被设计成这样,但我认为我的假设很弱(Iterable 是默认情况下获得特定实现的基本类型?;它可能与CollectionList 相交?;如果Iterable 返回一个无限迭代器怎么办?)。 您可以通过以下拉取请求追踪Iterable 支持问题:https://github.com/google/gson/pull/854

首先,为什么 Gson 对于您提到的两种情况(.toJson(bar).toJson(bar.list))表现不同。 对于第一种情况,Gson 扫描您的 bar 对象并使用反射直接从字段中检索类型信息,这就是它获取 Iterable 的地方。 对于第二种情况,Gson 没有 声明类型 信息,因此它只需要使用 .getClass() 丢失类型参数化的实际类(不是类型!)(字符串非常适合这个测试转换),@987654332 @ 在这种情况下,Gson 支持开箱即用。 请注意,后者也可以用gson.toJson(..., new TypeToken&lt;Iterable&lt;String&gt;&gt;() {}.getType(), System.out) 来重现,明确建议给定对象type(不是类!)。

不过,您可以自己添加Iterable 支持,但我不确定下面的实现是否需要更多工作:

final class IterableMyTypeAdapterFactory
        implements TypeAdapterFactory {

    private static final TypeAdapterFactory iterableTypeAdapterFactory = new IterableMyTypeAdapterFactory();

    private IterableMyTypeAdapterFactory() {
    }

    // It's an effective singleton but we do not reveal it
    static TypeAdapterFactory getIterableMyTypeAdapterFactory() {
        return iterableTypeAdapterFactory;
    }

    @Override
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
        // Must be an iterable, subclasses should be picked up by built-in type adapters
        if ( !Iterable.class.isAssignableFrom(typeToken.getRawType()) || Collection.class.isAssignableFrom(typeToken.getRawType()) ) {
            // Tell Gson to pick up on its own
            return null;
        }
        // Extract the element type. If it's raw, we assume java.lang.Object is in the play
        final Type elementType = getTypeParameter0(typeToken.getType());
        // Get the element type adapter
        final TypeAdapter<?> elementTypeAdapter = gson.getDelegateAdapter(this, TypeToken.get(elementType));
        // And get rid of wildcards
        @SuppressWarnings("unchecked")
        final TypeAdapter<Object> castElementTypeAdapter = (TypeAdapter<Object>) elementTypeAdapter;
        // Instantiating a new iterable type adapter
        final TypeAdapter<Iterable<Object>> iterableTypeAdapter = IterableTypeAdapter.get(castElementTypeAdapter);
        // Cast it cheating javac
        @SuppressWarnings("unchecked")
        final TypeAdapter<T> castTypeAdapter = (TypeAdapter<T>) iterableTypeAdapter;
        return castTypeAdapter;
    }

    private static Type getTypeParameter0(final Type type) {
        if ( !(type instanceof ParameterizedType) ) {
            return Object.class;
        }
        final ParameterizedType parameterizedType = (ParameterizedType) type;
        return parameterizedType.getActualTypeArguments()[0];
    }

    private static final class IterableTypeAdapter<E>
            extends TypeAdapter<Iterable<E>> {

        private final TypeAdapter<E> elementTypeAdapter;

        private IterableTypeAdapter(final TypeAdapter<E> elementTypeAdapter) {
            this.elementTypeAdapter = elementTypeAdapter;
        }

        private static <E> TypeAdapter<Iterable<E>> get(final TypeAdapter<E> elementTypeAdapter) {
            return new IterableTypeAdapter<>(elementTypeAdapter)
                    .nullSafe(); // We don't need to handle nulls ourselves anymore
        }

        @Override
        @SuppressWarnings("resource")
        public void write(final JsonWriter jsonWriter, final Iterable<E> elements)
                throws IOException {
            // Emit [
            jsonWriter.beginArray();
            for ( final E e : elements ) {
                // Write each element to the downstream writer
                elementTypeAdapter.write(jsonWriter, e);
            }
            // Emit ]
            jsonWriter.endArray();
        }

        @Override
        @SuppressWarnings("resource")
        public Iterable<E> read(final JsonReader jsonReader)
                throws IOException {
            jsonReader.beginArray(); // Expect [
            final Collection<E> elements = new ArrayList<>(); // This is probably why there is Iterable support by default
            while ( jsonReader.hasNext() ) {
                final E e = elementTypeAdapter.read(jsonReader); // Read each element
                elements.add(e);
            }
            jsonReader.endArray(); // Expect ]
            return elements;
        }

    }

}
private static final class Pack {

    Iterable<String> iterable;
    Collection<String> collection;
    List<String> list;
    Map<String, String> map;

}

private static final Gson gson = new GsonBuilder()
        .registerTypeAdapterFactory(getIterableMyTypeAdapterFactory())
        .create();

public static void main(final String... args) {
    final List<String> fooBar = ImmutableList.of("foo", "bar");
    final Pack pack = new Pack();
    pack.iterable = fooBar;
    pack.collection = fooBar;
    pack.list = fooBar;
    pack.map = ImmutableMap.of("foo", "bar");
    gson.toJson(pack, System.out);
}

之前的输出:

{"iterable":{},"collection":["foo","bar"],"list":["foo","bar"],"map":{"foo":"bar"}}

之后的输出:

{"iterable":["foo","bar"],"collection":["foo","bar"],"list":["foo","bar"],"map":{"foo":"bar"}}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-15
    • 2012-02-02
    • 1970-01-01
    • 2021-06-17
    相关资源
    最近更新 更多