2017-05-03 96 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类型,现在它可以工作,今天我会测试一下。 –