Gson使用TypeAdapter或Json Deserializer将数据从错误列表转换为空列表 [英] Gson uses TypeAdapter or Json Deserializer to convert data from an error list to an empty list
问题描述
让我们从示例开始:
如果数据正确,则应该为(Beijing
cities
为空)
If the data is correct, it should be ( the Beijing
cities
is empty )
{
"code":200,
"msg":"success",
"data":[
{
"id":1,
"name":"Beijing",
"cities":[]
},
{
"id":2,
"name":"Guangdong",
"cities":[
{
"id":1,
"name":"Guangzhou"
}
]
}
]
}
现在我得到了错误的数据. (Beijing
cities
为null)
Now I got a wrong data. ( the Beijing
cities
is null )
{
"code":200,
"msg":"success",
"data":[
{
"id":1,
"name":"Beijing",
"cities":null
},
{
"id":2,
"name":"Guangdong",
"cities":[
{
"id":1,
"name":"Guangzhou"
}
]
}
]
}
我正在使用Retrofit2
ResponseBodyConverter
实体类:
public class Result<T> {
private int code;
private String msg;
private T data;
// getters, setters
}
public class Province {
private int id;
private String name;
private List<City> cities;
}
public class City {
private int id;
private String name;
}
反序列化后获得的数据如下:
The data obtained after deserialization is like this:
但是我需要的数据是这样的:
but the data I need is like this:
为了具有更好的容错能力,当列出数据时,我想自己处理.
首先,我尝试使用JsonDeserializer
In order to have better fault tolerance, when the data is list, I want to process it by myself.
First of all,I tried to use JsonDeserializer
Gson gson = new GsonBuilder()
.serializeNulls()
.registerTypeHierarchyAdapter(List.class, new GsonListAdapter())
.create();
static class GsonListAdapter implements JsonDeserializer<List<?>> {
@Override
public List<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json.isJsonArray()) {
JsonArray array = json.getAsJsonArray();
Type itemType = ((ParameterizedType) typeOfT).getActualTypeArguments()[0];
List list = new ArrayList<>();
for (int i = 0; i < array.size(); i++) {
JsonElement element = array.get(i);
Object item = context.deserialize(element, itemType);
list.add(item);
}
return list;
} else {
return Collections.EMPTY_LIST;
}
}
}
JsonDeserializer
在数据为""
,{}
和[]
时有效,但data
为null
时将不起作用.
JsonDeserializer
is valid when the data is ""
, {}
, and []
,but data
is null
, it will not work.
然后我尝试使用TypeAdapter
static class GsonListAdapter extends TypeAdapter<List<?>> {
@Override
public void write(JsonWriter out, List<?> value) throws IOException {
out.value(String.valueOf(value));
}
@Override
public List<?> read(JsonReader reader) throws IOException {
if (reader.peek() != JsonToken.BEGIN_ARRAY) {
reader.skipValue();
return Collections.EMPTY_LIST;
}
return new Gson().fromJson(reader, new TypeToken<List<?>>() {}.getType());
}
}
这样,无论data
是什么,它都可以正常工作.我们知道使用TypeToken<List<?>>
会给我们LinkedHashMap
,因此,尽管TypeAdapter
可以正常工作,但我不能知道如何将JsonReader
转换为List <?>
.
In this way, no matter what the data
is, it can work properly.We know that using TypeToken<List<?>>
will give us the LinkedHashMap
,So although TypeAdapter
can work properly, but I don't know how to convert JsonReader
to the List <?>
.
所以我想知道是否还有其他方法可以处理错误的列表数据?或将JsonReader
转换为我想要的List <?> data
.
So I wonder if there are other ways that I can handle the wrong list data? Or convert JsonReader
to the List <?> data
I want.
推荐答案
我在Gson
源代码中找到了CollectionTypeAdapterFactory
.我试图对其进行修改,它已经过测试,非常有用.
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 <T> TypeAdapter<T> create(Gson gson, TypeToken<T> 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<T> constructor = constructorConstructor.get(typeToken);
@SuppressWarnings({"unchecked", "rawtypes"}) // create() doesn't define a type parameter
TypeAdapter<T> result = new Adapter(gson, elementType, elementTypeAdapter, constructor);
return result;
}
private static final class Adapter<E> extends TypeAdapter<Collection<E>> {
private final TypeAdapter<E> elementTypeAdapter;
private final ObjectConstructor<? extends Collection<E>> constructor;
public Adapter(Gson context, Type elementType,
TypeAdapter<E> elementTypeAdapter,
ObjectConstructor<? extends Collection<E>> constructor) {
this.elementTypeAdapter =
new TypeAdapterRuntimeTypeWrapper<E>(context, elementTypeAdapter, elementType);
this.constructor = constructor;
}
public Collection<E> 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<E> 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<E> collection) throws IOException {
if (collection == null) {
out.nullValue();
return;
}
out.beginArray();
for (E element : collection) {
elementTypeAdapter.write(out, element);
}
out.endArray();
}
}
}
在源代码中TypeAdapterRuntimeTypeWrapper
受保护,我们必须进行复制.
In the source code the TypeAdapterRuntimeTypeWrapper
is protected,We must make a copy.
public class TypeAdapterRuntimeTypeWrapper<T> extends TypeAdapter<T> {
private final Gson context;
private final TypeAdapter<T> delegate;
private final Type type;
TypeAdapterRuntimeTypeWrapper(Gson context, TypeAdapter<T> 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;
}
}
使用方法
Gson gson = new GsonBuilder().serializeNulls()
.registerTypeAdapterFactory(
new CollectionTypeAdapterFactory(new ConstructorConstructor(new HashMap<>()))
)
.create();
Result<List<Province>> result = gson.fromJson(jsonStr, new TypeToken<Result<List<Province>>>() {}.getType());
打印:
Result{code=200, msg='success', data=[Province{id=1, name='Beijing', cities=[]}, Province{id=2, name='Guangdong', cities=[City{id=1, name='Guangzhou'}]}]}
这篇关于Gson使用TypeAdapter或Json Deserializer将数据从错误列表转换为空列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!