2017-05-03 43 views
0

我正在android上使用Retrofit和Gson構建一個reddit客戶端來發出API請求。我做了檢索徵求意見後Gson反序列化reddit評論android上的API響應

https://www.reddit.com/r/pics/comments/68pxct/black_out_panels_with_a_twist/.json

的請求雖然deserialising我遇到了一個問題的響應,響應有一個字段children返回一個List<CustomObject>有時等一個List<String>倍。

我嘗試了多個選項,使用JsonAdapter與自定義JsonDeserializer,沒有工作,然後嘗試使用它與TypeAdapter,仍然沒有工作。

我場是這樣,

@Expose 
@JsonAdapter(CustomDeserializer.class) 
private List<T> children; 

在這兩種這些情況下,我打了,我用與泛型類的類未發現異常。我在Deserializer中有斷點,它甚至沒有開始執行代碼。評論@JsonAdapter註釋導致類未找到異常消失。

我然後試圖接收PARAMS爲通用JsonArray型,但仍然遇到錯誤

@Expose 
private JsonArray children; 

所致:java.lang.IllegalStateException:預期BEGIN_OBJECT但在第1行的列210200路徑STRING $ [1] .data.children [12] .data.replies

爲什麼我無法將它轉換爲JsonArray。我認爲這應該工作,不管內在類型是什麼。

理想情況下,我想讓JsonAdapter方法工作。

編輯:

之所以將其轉換爲JsonArry沒有工作是因爲有另一場(replies),這也返回了多個數據類型。這應該是顯而易見的錯誤,我猜它已經過去了我的睡前時間。所以將字段轉換爲JsonArray和JsonObject。

也有人建議泛型不能很好地使用JsonAdapter註釋,我會測試其他解決方案並更新回來。

+0

的'答覆'在你的數據中以JSON中的一個引號開始,而不是一個對象'{}' –

+0

你可以在JSON中搜索'「回覆」:「''' –

+0

@ cricket_007確實,回覆確實是這樣開始的。但是因爲我將子對象反序列化爲一個JsonArray,這應該不重要? –

回答

1

您不能使用<T>,因爲Gson無法爲深度嵌套的子對象檢索足夠的類型信息。你可以做的是一種對齊方式,將「自定義對象」和字符串對齊在一起,這樣你就可以輕鬆控制這兩種類型。

假設你可能有這樣的事情:

// Not an interface by design: it's most likely there is just two known data types 
abstract class Element { 

    // So we can control they instantiation 
    private Element() { 
    } 

    // ... any convenient code, visitor pattern stuff here, etc .. 

    static Element reference(final String reference) { 
     return new ReferenceElement(reference); 
    } 

    static final class DataElement 
      extends Element { 

     final String kind = null; 
     final Data data = null; 

     // Gson does requires neither constructors nor making them non-private 
     private DataElement() { 
     } 

    } 

    // This is a special wrapper because we cannot make java.util.String to be a subclass of the Element class 
    // Additionally, you can add more methods if necessary 
    static final class ReferenceElement 
      extends Element { 

     final String reference; 

     // But anyway, control the way it's instantiated within the enclosing class 
     private ReferenceElement(final String reference) { 
      this.reference = reference; 
     } 

    } 

} 

由於我不熟悉的reddit的API,我假設它可以與下面的類特定的反應映射:

final class Data { 

    final List<Element> children = null; 
    final Element replies = null; 

} 
final class ElementTypeAdapterFactory 
     implements TypeAdapterFactory { 

    // Effectively a singleton, no state, fully thread-safe, etc  
    private static final TypeAdapterFactory elementTypeAdapterFactory = new ElementTypeAdapterFactory(); 

    private static final TypeToken<DataElement> dateElementTypeToken = new TypeToken<DataElement>() { 
    }; 

    private ElementTypeAdapterFactory() { 
    } 

    // So just return the single instance and hide away the way it's instantiated 
    static TypeAdapterFactory getElementTypeAdapterFactory() { 
     return elementTypeAdapterFactory; 
    } 

    @Override 
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) { 
     // Not the Element class? 
     if (!Element.class.isAssignableFrom(typeToken.getRawType())) { 
      // Then just let Gson pick up the next best type adapter 
      return null; 
     } 
     // 
     final TypeAdapter<DataElement> dataElementTypeAdapter = gson.getDelegateAdapter(this, dateElementTypeToken); 
     @SuppressWarnings("unchecked") 
     final TypeAdapter<T> typeAdapter = (TypeAdapter<T>) new ElementTypeAdapter(dataElementTypeAdapter); 
     return typeAdapter.nullSafe(); 
    } 

    private static final class ElementTypeAdapter 
      extends TypeAdapter<Element> { 

     private final TypeAdapter<DataElement> dataTypeAdapter; 

     private ElementTypeAdapter(final TypeAdapter<DataElement> dataTypeAdapter) { 
      this.dataTypeAdapter = dataTypeAdapter; 
     } 

     @Override 
     public void write(final JsonWriter out, final Element value) 
       throws IOException { 
      if (value instanceof DataElement) { 
       dataTypeAdapter.write(out, (DataElement) value); 
      } else if (value instanceof ReferenceElement) { 
       out.value(((ReferenceElement) value).reference); 
      } else { 
       // null-protection is configured with .nullSafe() above 
       throw new AssertionError(value.getClass()); 
      } 
     } 

     @Override 
     public Element read(final JsonReader in) 
       throws IOException { 
      final JsonToken token = in.peek(); 
      switch (token) { 
      case BEGIN_OBJECT: 
       return dataTypeAdapter.read(in); 
      case STRING: 
       return reference(in.nextString()); 
      case BEGIN_ARRAY: 
      case END_ARRAY: 
      case END_OBJECT: 
      case NAME: 
      case NUMBER: 
      case BOOLEAN: 
      case NULL: // null-protection is configured with .nullSafe() above 
      case END_DOCUMENT: 
       throw new MalformedJsonException("Cannot parse " + token + " at " + in); 
      default: 
       // If someday there are more tokens... 
       throw new AssertionError(token); 
      } 
     } 

    } 

} 

現在把所有這些組合起來:

private static final Type type = new TypeToken<List<Element>>() { 
    }.getType(); 

    private static final Gson gson = new GsonBuilder() 
      .registerTypeAdapterFactory(getElementTypeAdapterFactory()) 
      .create(); 

    public static void main(final String... args) 
      throws IOException { 
     try (final Reader reader = getPackageResourceReader(Q43764362.class, "reddit.json")) { 
      final List<Element> elements = gson.fromJson(reader, type); 
      dump(elements); 
     } 
    } 

    private static void dump(final Iterable<Element> abstractElements) { 
     dump(abstractElements, 0); 
    } 

    private static void dump(final Iterable<Element> abstractElements, final int level) { 
     final String tab = repeat(".", level); 
     for (final Element e : abstractElements) { 
      if (e instanceof DataElement) { 
       final DataElement dataElement = (DataElement) e; 
       System.out.print(tab); 
       System.out.print("DATA="); 
       System.out.println(dataElement.kind); 
       if (dataElement.data.children != null) { 
        dump(dataElement.data.children, level + 1); 
       } 
       if (dataElement.data.replies != null) { 
        final Element replies = dataElement.data.replies; 
        if (dataElement.data.replies instanceof DataElement) { 
         dump(((DataElement) replies).data.children, level + 1); 
        } else if (dataElement.data.replies instanceof ReferenceElement) { 
         System.out.print(tab); 
         System.out.print("REF="); 
         System.out.println(((ReferenceElement) dataElement.data.replies).reference); 
        } else { 
         throw new AssertionError(replies.getClass()); 
        } 
       } 
      } else if (e instanceof ReferenceElement) { 
       System.out.print(tab); 
       System.out.print("REF="); 
       System.out.println(((ReferenceElement) e).reference); 
      } else { 
       throw new IllegalArgumentException(String.valueOf(e)); 
      } 
     } 
    } 
從電流響應

輸出摘錄:

DATA =清單
.DATA = t3時
DATA =清單
.DATA = t1時
..DATA = t1時
... DATA = t1時
.... DATA = T1
..... DATA = T1
...... DATA = T1
....... DATA =更多
........ REF = dh0x8h5
..... DATA =更
...... REF = dh11148
...... REF = dh19yft

.. REF = dh0pcjh
..REF = dh0n73y
..REF = dh0kp1r
..REF = dh0mg9c
..REF = dh0i6z5
..REF = dh0inc3
..REF = dh0oyc4
..REF = dh0phb0
..REF = dh0ln22
..REF = dh0wjqa
..REF = dh0q48s
..REF = dh0tfjl
..REF = dh0kauq
..REF = dh0rxtf
..REF = dh0led3

+0

謝謝。您的解決方案看起來非常適合我的需求。我將這兩個字段都更改爲JsonObject和JsonArray類型,現在它可以工作,今天我會測試一下。 –