Gson反序列化reddit评论Android

时间:2017-05-03 15:32:16

标签: android gson retrofit android-json

我正在android上构建一个reddit客户端,使用Retrofit和Gson来发出API请求。我提出了检索帖子

的评论的请求
  

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

在对响应进行反序列化时遇到了问题,响应中有一个字段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但是STRING在第1行第210200行$ [1] .data.children [12] .data.replies

为什么我无法将其转换为JsonArray。我认为无论内在类型是什么都应该有效。

理想情况下,我希望让JsonAdapter方法正常工作。

编辑:

将其转换为JsonArry的原因不起作用是因为还有另一个字段(replies)也返回了多种数据类型。应该很明显地看错误,我想这已经过了我的睡觉时间。因此,将字段转换为JsonArray和JsonObject是有效的。

还有人建议泛型不能很好地使用JsonAdapter注释,我会测试其他解决方案并更新回来。

1 个答案:

答案 0 :(得分: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