2017-05-12 30 views
0

我正在使用Retrofit + OkHttp連接到REST API並使用JSON數據的Android應用程序。我對改造相當陌生,所以我仍然在學習它是如何工作的,但到目前爲止它一直非常流暢。但是,我遇到了一個不尋常的問題。改造:如何解析組合數組和對象的JSON數組?

一個看起來有點像這樣的端點返回的數據:

{ 
    "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-> POJO解析的優點。

有沒有一種優雅的方式來處理像Retrofit/Gson這樣的混合數組?我不對來自API的數據負責,所以我不認爲改變這將是一個選擇。

回答

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(); 
+0

啊哈!我知道必須有解決方案。謝謝;我會試試這個。 –

+0

增加了更多的初始化代碼 – LukeJanyga

1

你似乎已經不使用List<MyObject>,因爲你必須建立在兩種以上對象:「單個」和「多個」,後者必須實現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