我正在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注释,我会测试其他解决方案并更新回来。
答案 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