你不能使用<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
...数据=t1
....DATA=t1
.....数据=t1
......数据=t1
.......数据=更多
........REF=dh0x8h5
.....数据=更多
......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