[英]Can't properly serialize POJO with Iterable fields using Gson
Here is my POJO I am trying to serialize: 这是我要序列化的POJO:
public class Bar {
private final Foo foo;
private final Iterable<String> list;
private final Iterable<Map<String, String>> listOfMaps;
}
Here is how I'm calling it 这是我的称呼方式
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);
Here is the result 这是结果
{"foo":{"field1":"val1","field2":"val2"},"list":{},"listOfMaps":{}}
As you can see, the POJO serializes fine, but the iterable (instance of guava collections) doesn't serialize to JSON properly. 如您所见,POJO可以很好地序列化,但是可迭代(番石榴集合的实例)不能正确地序列化为JSON。 When I serialize the fields on their own, they show up fine, but it won't properly serialize when they are fields of
Bar
当我自行序列化字段时,它们显示很好,但是当它们是
Bar
字段时,它将无法正确序列化
Example: 例:
new Gson().toJson(bar.list);
["fooList"]
It looks like an old issue for Gson and this is how it was initially designed. 对于Gson来说,这似乎是一个老问题,这就是最初设计的方式。 I have some assumptions why it might be designed like that, but I think my assumptions are weak (
Iterable
is just too base type to get a particular implementation by default?; it may intersect with Collection
and List
?; what if Iterable
returns an infinite iterator?). 我有一些假设,为什么可能会这样设计,但是我认为我的假设很弱(
Iterable
太基类型,默认情况下无法获得特定实现?;它可能与Collection
和List
相交?;如果Iterable
返回无限迭代器?)。 You can track down the Iterable
support issue through this pull request: https://github.com/google/gson/pull/854 . 您可以通过以下拉取请求跟踪
Iterable
支持问题: https : //github.com/google/gson/pull/854 。
First of all, why Gson behaves differently for both cases you mentioned ( .toJson(bar)
and .toJson(bar.list)
). 首先,为什么Gson在您提到的两种情况下都有不同的行为(
.toJson(bar)
和.toJson(bar.list)
)。 For the first case, Gson scans your bar
object and it retrieves type information directly from the fields using reflection, and this is where it gets Iterable
. 对于第一种情况,Gson扫描您的
bar
对象,并使用反射直接从字段中检索类型信息,这就是它获取Iterable
。 For the second case, Gson does not have declaration type information therefore it just takes the actual class (not type!) using .getClass()
losing type parameterization (strings fit this test cast just perfect), Collection
in this case that has Gson support out of box. 对于第二种情况,Gson没有声明类型信息,因此仅使用
.getClass()
来.getClass()
实际的类(而不是类型! .getClass()
丢失类型参数化(字符串适合此测试.getClass()
是完美的),在这种情况下, Collection
具有Gson支持开箱即用。 Note that the latter can also be reproduced with gson.toJson(..., new TypeToken<Iterable<String>>() {}.getType(), System.out)
suggesting the given object type (not class!) explicitly. 请注意,后者也可以通过
gson.toJson(..., new TypeToken<Iterable<String>>() {}.getType(), System.out)
进行复制, gson.toJson(..., new TypeToken<Iterable<String>>() {}.getType(), System.out)
明确建议给定的对象类型 (而不是类!)。
However, you can add Iterable
support yourself, but I'm not sure if the implementation below requires more work: 但是,您可以自己添加
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);
}
Output before: 输出之前:
{"iterable":{},"collection":["foo","bar"],"list":["foo","bar"],"map":{"foo":"bar"}}
Output after: 输出后:
{"iterable":["foo","bar"],"collection":["foo","bar"],"list":["foo","bar"],"map":{"foo":"bar"}}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.