Let's start from example:
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"
}
]
}
]
}
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"
}
]
}
]
}
I am using the Retrofit2
ResponseBodyConverter
,the entity class:
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:
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
is valid when the data is ""
, {}
, and []
,but data
is null
, it will not work.
Then I tried to use 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());
}
}
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 <?>
.
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.
I found the
CollectionTypeAdapterFactory
inGson
source code.I tried to modify it,it has been tested and it is useful.In the source code the
TypeAdapterRuntimeTypeWrapper
is protected,We must make a copy.How to use
prints: