对于 Gson 来说,这似乎是一个老问题,这就是它最初的设计方式。
我有一些假设为什么它可能会被设计成这样,但我认为我的假设很弱(Iterable 是默认情况下获得特定实现的基本类型?;它可能与Collection 和List 相交?;如果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<Iterable<String>>() {}.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"}}