我正在使用一个Android应用程序,该应用程序使用Retrofit + OkHttp连接到REST API并使用JSON数据。我对Retrofit很新,所以我还在学习它是如何工作的,但到目前为止它一直很顺利。但是,我遇到了一个不寻常的问题。
其中一个端点返回看起来有点像这样的数据:
{
"success": true,
"data": [
[
{
"field": "value1",
...
},
{
"field": "value2",
...
},
...
],
{
"info": "value",
"info2": "value",
"info3": "value",
}
],
"message": "This is the message."
}
在大多数情况下,这是一个非常标准的JSON响应。我们有一个“状态”值,一个“消息”和一个包含要返回的重要数据的“数据”值。但是,“数据”的结构存在问题。如您所见,它是一个数组,但它不仅仅是一个对象数组。它的第一个元素是值数组,第二个元素是对象。
Gson不喜欢这个。如果我想创建一个POJO来解析它使用Gson,我希望做这样的事情:
public class JsonResponse {
@SerializedName("success")
@Expose
boolean success;
@SerializedName("message")
@Expose
String message;
@SerializedName("data")
@Expose
private ArrayList<MyObject> data = new ArrayList<>();
public ArrayList<MyObject> getData() {
return data;
}
public void setData(ArrayList<MyObject> data) {
this.data = data;
}
}
其中“MyObject”是这样的Parcelable:
public class MyObject implements Parcelable {
@SerializedName("field")
@Expose
boolean field;
...
}
但这不起作用,因为“数据”不仅仅是一个对象数组;它是一个包含对象数组和另一个顶级对象的数组。
我可以将“data”定义为“Array”,它似乎解析了这些值。但后来它们作为通用的LinkedTreeMap对象出现,所以我失去了Gson的JSON-&gt; POJO解析的优势。
在Retrofit / Gson中是否有一种优雅的方式来处理这样的混合数组?我不对来自API的数据负责,所以我认为改变它不会是一种选择。
答案 0 :(得分:1)
有办法。
添加自定义JsonDeserializer
并手动处理该字段。
实施例
public static class Deserializer implements JsonDeserializer<JsonResponse> {
private final Gson gson = new GsonBuilder().create();
@Override
public JsonResponse deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context)
throws JsonParseException {
JsonResponse response = gson.fromJson(json, typeOfT);
JsonObject jObject = json.getAsJsonObject();
//Handle jObject here, and parse each object of data array
//accordingly to its type - JsonObject/JsonArray
return response;
}
}
您需要在全局Gson实例中注册它,如下所示:
new GsonBuilder()
.registerTypeAdapter(JsonResponse.class, new JsonResponse.Deserializer())
.create();
答案 1 :(得分:1)
您似乎没有使用List<MyObject>
,因为您必须创建至少两种对象:“single”和“multiple”,后者必须实现List<E>
以满足类似数组的接口
例如。 JsonResponse
比jsonschema2pojo生成的更简单:
final class JsonResponse {
final boolean success = Boolean.valueOf(false);
final String message = null;
final List<MyObject> data = null;
}
现在,MyObject
可能如下所示:
abstract class MyObject
implements Parcelable {
private MyObject() {
}
static <E> MyObject multiple(final List<E> list) {
return new MultipleObjects<>(list);
}
static final class SingleObject
extends MyObject {
private SingleObject() {
}
final String info = null;
final String info2 = null;
final String info3 = null;
}
static final class MultipleObjects<E>
extends MyObject
implements List<E> {
private final List<E> list;
private MultipleObjects(final List<E> list) {
this.list = list;
}
// @formatter:off
@Override public int size() { return list.size(); }
@Override public boolean isEmpty() { return list.isEmpty(); }
@Override public boolean contains(final Object o) { return list.contains(o); }
@Override public Iterator<E> iterator() { return list.iterator(); }
@Override public Object[] toArray() { return list.toArray(); }
@Override public <T> T[] toArray(final T[] a) { return list.toArray(a); }
@Override public boolean add(final E e) { return list.add(e); }
@Override public boolean remove(final Object o) { return list.remove(o); }
@Override public boolean containsAll(final Collection<?> c) { return list.containsAll(c); }
@Override public boolean addAll(final Collection<? extends E> c) { return list.addAll(c); }
@Override public boolean addAll(final int index, final Collection<? extends E> c) { return list.addAll(index, c); }
@Override public boolean removeAll(final Collection<?> c) { return list.removeAll(c); }
@Override public boolean retainAll(final Collection<?> c) { return list.retainAll(c); }
@Override public void clear() { list.clear(); }
@Override public E get(final int index) { return list.get(index); }
@Override public E set(final int index, final E element) { return list.set(index, element); }
@Override public void add(final int index, final E element) { list.add(index, element); }
@Override public E remove(final int index) { return list.remove(index); }
@Override public int indexOf(final Object o) { return list.indexOf(o); }
@Override public int lastIndexOf(final Object o) { return list.lastIndexOf(o); }
@Override public ListIterator<E> listIterator() { return list.listIterator(); }
@Override public ListIterator<E> listIterator(final int index) { return list.listIterator(index); }
@Override public List<E> subList(final int fromIndex, final int toIndex) { return list.subList(fromIndex, toIndex); }
// @formatter:on
}
}
上面的类实现了一个可以用两种方式实现的抽象类。请注意,设计不会公开任何公共构造函数:SingleObject
可以使用反射策略使用Gson进行反序列化,而MultipleObjects
是一个类似于数组的对象,需要手动构建。
反序列化部分:
final class MyObjectJsonDeserializer
implements JsonDeserializer<MyObject> {
private static final JsonDeserializer<MyObject> myObjectJsonDeserializer = new MyObjectJsonDeserializer();
// You have to detect it more accurately yourself
private static final Type genericListType = new TypeToken<List<Object>>() {
}.getType();
private MyObjectJsonDeserializer() {
}
static JsonDeserializer<MyObject> getMyObjectJsonDeserializer() {
return myObjectJsonDeserializer;
}
@Override
public MyObject deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context)
throws JsonParseException {
if ( jsonElement.isJsonNull() ) {
return null;
}
if ( jsonElement.isJsonObject() ) {
// Note that the deserialization is implemented using context,
// because it makes sure that you are using the Gson instance configuration
// Simply speaking: do not create gson instances in 99,9% cases
return context.deserialize(jsonElement, MyObject.SingleObject.class);
}
if ( jsonElement.isJsonArray() ) {
return multiple(context.deserialize(jsonElement, genericListType));
}
// Or create a more sophisticated detector... Or redesign your mappigns
if ( jsonElement.isJsonPrimitive() ) {
throw new JsonParseException("Cannot parse primitives");
}
throw new AssertionError(jsonElement);
}
}
使用示例:
private static final Gson gson = new GsonBuilder()
.registerTypeAdapter(MyObject.class, getMyObjectJsonDeserializer())
.create();
public static void main(final String... args)
throws IOException {
try ( final JsonReader jsonReader = getPackageResourceJsonReader(Q43946453.class, "polymorphic.json") ) {
final JsonResponse response = gson.fromJson(jsonReader, JsonResponse.class);
for ( final MyObject datum : response.data ) {
System.out.println(datum.getClass().getSimpleName());
}
}
}
输出:
MultipleObjects
SingleObject