Let\'s start from example:
If the data is correct, it should be ( the Beijing
cities
is empty )
{
\"code\":200,
\"msg\":\
I found the CollectionTypeAdapterFactory
in Gson
source code.I tried to modify it,it has been tested and it is useful.
public class CollectionTypeAdapterFactory implements TypeAdapterFactory {
private final ConstructorConstructor constructorConstructor;
public CollectionTypeAdapterFactory(ConstructorConstructor constructorConstructor) {
this.constructorConstructor = constructorConstructor;
}
public TypeAdapter create(Gson gson, TypeToken typeToken) {
Type type = typeToken.getType();
Class super T> rawType = typeToken.getRawType();
if (!Collection.class.isAssignableFrom(rawType)) {
return null;
}
Type elementType = $Gson$Types.getCollectionElementType(type, rawType);
TypeAdapter> elementTypeAdapter = gson.getAdapter(TypeToken.get(elementType));
ObjectConstructor constructor = constructorConstructor.get(typeToken);
@SuppressWarnings({"unchecked", "rawtypes"}) // create() doesn't define a type parameter
TypeAdapter result = new Adapter(gson, elementType, elementTypeAdapter, constructor);
return result;
}
private static final class Adapter extends TypeAdapter> {
private final TypeAdapter elementTypeAdapter;
private final ObjectConstructor extends Collection> constructor;
public Adapter(Gson context, Type elementType,
TypeAdapter elementTypeAdapter,
ObjectConstructor extends Collection> constructor) {
this.elementTypeAdapter =
new TypeAdapterRuntimeTypeWrapper(context, elementTypeAdapter, elementType);
this.constructor = constructor;
}
public Collection read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
//In the source code is return null, I changed to return an empty collection
return constructor.construct();
}
Collection collection = constructor.construct();
in.beginArray();
while (in.hasNext()) {
E instance = elementTypeAdapter.read(in);
collection.add(instance);
}
in.endArray();
return collection;
}
public void write(JsonWriter out, Collection collection) throws IOException {
if (collection == null) {
out.nullValue();
return;
}
out.beginArray();
for (E element : collection) {
elementTypeAdapter.write(out, element);
}
out.endArray();
}
}
}
In the source code the TypeAdapterRuntimeTypeWrapper
is protected,We must make a copy.
public class TypeAdapterRuntimeTypeWrapper extends TypeAdapter {
private final Gson context;
private final TypeAdapter delegate;
private final Type type;
TypeAdapterRuntimeTypeWrapper(Gson context, TypeAdapter delegate, Type type) {
this.context = context;
this.delegate = delegate;
this.type = type;
}
@Override
public T read(JsonReader in) throws IOException {
return delegate.read(in);
}
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
public void write(JsonWriter out, T value) throws IOException {
TypeAdapter chosen = delegate;
Type runtimeType = getRuntimeTypeIfMoreSpecific(type, value);
if (runtimeType != type) {
TypeAdapter runtimeTypeAdapter = context.getAdapter(TypeToken.get(runtimeType));
if (!(runtimeTypeAdapter instanceof ReflectiveTypeAdapterFactory.Adapter)) {
// The user registered a type adapter for the runtime type, so we will use that
chosen = runtimeTypeAdapter;
} else if (!(delegate instanceof ReflectiveTypeAdapterFactory.Adapter)) {
// The user registered a type adapter for Base class, so we prefer it over the
// reflective type adapter for the runtime type
chosen = delegate;
} else {
// Use the type adapter for runtime type
chosen = runtimeTypeAdapter;
}
}
chosen.write(out, value);
}
private Type getRuntimeTypeIfMoreSpecific(Type type, Object value) {
if (value != null
&& (type == Object.class || type instanceof TypeVariable> || type instanceof Class>)) {
type = value.getClass();
}
return type;
}
}
How to use
Gson gson = new GsonBuilder().serializeNulls()
.registerTypeAdapterFactory(
new CollectionTypeAdapterFactory(new ConstructorConstructor(new HashMap<>()))
)
.create();
Result> result = gson.fromJson(jsonStr, new TypeToken>>() {}.getType());
prints:
Result{code=200, msg='success', data=[Province{id=1, name='Beijing', cities=[]}, Province{id=2, name='Guangdong', cities=[City{id=1, name='Guangzhou'}]}]}