我有一群具有类型的字段的模型类的List<X>地方X是很多的事情之一(例如String,Integer,但也有一些我自己的类型)。我正在使用GSON解析这些模型的JSON表示形式。
List<X>
X
String
Integer
我的问题是,我正在处理的服务器(超出我的控制范围)以某种方式删除了单例数组,并将其替换为包含的对象。
例如,不返回:
{ "foo": [ "bar"], "bleh": [ { "some": "object" } ] }
它返回:
{ "foo": "bar", "bleh": { "some": "object" } }
现在假设Java模型类看起来像这样:
public class Model { private List<String> foo; private List<SomeObject> bleh; }
当前,这会导致GSON抛出异常,因为它找到了BEGIN_STRING或BEGIN_OBJECT期望到了什么地方BEGIN_ARRAY。
BEGIN_STRING
BEGIN_OBJECT
BEGIN_ARRAY
对于字符串数组或字符串列表,可以使用轻松解决TypeAdapter<List<String>>。但是问题是我有List许多不同的元素类型,并且我不想TypeAdapter为每种情况编写单独的元素。我也无法使用泛型TypeAdapter<List<?>>,因为在某些时候您需要知道类型。那么,还有另一种方法可以将GSON配置为足够聪明,以将单个对象或值转换为数组/列表吗?或者换句话说,只是“假装” the [and ]在那里,尽管它们不在那里,但它期望找到它们?
TypeAdapter<List<String>>
List
TypeAdapter
TypeAdapter<List<?>>
[
]
但是问题是我的List具有许多不同的元素类型,并且我不想为每种情况编写单独的TypeAdapter。我也无法使用通用的TypeAdapter>,因为在某些时候您需要知道类型。
这就是类型适配器工厂设计的目的:您可以控制Gson实例配置中的每种类型。
Gson
final class AlwaysListTypeAdapterFactory<E> implements TypeAdapterFactory { // Gson can instantiate it itself private AlwaysListTypeAdapterFactory() { } @Override public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) { // If it's not a List -- just delegate the job to Gson and let it pick the best type adapter itself if ( !List.class.isAssignableFrom(typeToken.getRawType()) ) { return null; } // Resolving the list parameter type final Type elementType = resolveTypeArgument(typeToken.getType()); @SuppressWarnings("unchecked") final TypeAdapter<E> elementTypeAdapter = (TypeAdapter<E>) gson.getAdapter(TypeToken.get(elementType)); // Note that the always-list type adapter is made null-safe, so we don't have to check nulls ourselves @SuppressWarnings("unchecked") final TypeAdapter<T> alwaysListTypeAdapter = (TypeAdapter<T>) new AlwaysListTypeAdapter<>(elementTypeAdapter).nullSafe(); return alwaysListTypeAdapter; } private static Type resolveTypeArgument(final Type type) { // The given type is not parameterized? if ( !(type instanceof ParameterizedType) ) { // No, raw return Object.class; } final ParameterizedType parameterizedType = (ParameterizedType) type; return parameterizedType.getActualTypeArguments()[0]; } private static final class AlwaysListTypeAdapter<E> extends TypeAdapter<List<E>> { private final TypeAdapter<E> elementTypeAdapter; private AlwaysListTypeAdapter(final TypeAdapter<E> elementTypeAdapter) { this.elementTypeAdapter = elementTypeAdapter; } @Override public void write(final JsonWriter out, final List<E> list) { throw new UnsupportedOperationException(); } @Override public List<E> read(final JsonReader in) throws IOException { // This is where we detect the list "type" final List<E> list = new ArrayList<>(); final JsonToken token = in.peek(); switch ( token ) { case BEGIN_ARRAY: // If it's a regular list, just consume [, <all elements>, and ] in.beginArray(); while ( in.hasNext() ) { list.add(elementTypeAdapter.read(in)); } in.endArray(); break; case BEGIN_OBJECT: case STRING: case NUMBER: case BOOLEAN: // An object or a primitive? Just add the current value to the result list list.add(elementTypeAdapter.read(in)); break; case NULL: throw new AssertionError("Must never happen: check if the type adapter configured with .nullSafe()"); case NAME: case END_ARRAY: case END_OBJECT: case END_DOCUMENT: throw new MalformedJsonException("Unexpected token: " + token); default: throw new AssertionError("Must never happen: " + token); } return list; } } }
现在,您只需要告诉Gson 哪些 字段格式不正确。当然,您可以将整个Gson实例配置为接受此类列表,但是使用@JsonAdapter批注使其更为精确:
@JsonAdapter
final class Model { @JsonAdapter(AlwaysListTypeAdapterFactory.class) final List<String> foo = null; @JsonAdapter(AlwaysListTypeAdapterFactory.class) final List<SomeObject> bleh = null; @Override public String toString() { return "Model{" + "foo=" + foo + ", bleh=" + bleh + '}'; } } final class SomeObject { final String some = null; @Override public String toString() { return "SomeObject{" + "some='" + some + '\'' + '}'; } }
测试数据:
{ "foo": "bar", "bleh": {"some": "object"} }
{ "foo": ["bar"], "bleh": [{"some": "object"}] }
例:
private static final Gson gson = new Gson(); public static void main(final String... args) throws IOException { for ( final String resource : ImmutableList.of("single.json", "list.json") ) { try ( final JsonReader jsonReader = getPackageResourceJsonReader(Q43412261.class, resource) ) { final Model model = gson.fromJson(jsonReader, Model.class); System.out.println(model); } } }
并输出:
模型{foo = [bar],bleh = [SomeObject {some =’object’}]}} 模型{foo = [bar],bleh = [SomeObject {some =’object’}]}}