使GSON接受它期望数组的单个对象

我有一堆模型类,它们具有List类型的字段,其中X是许多事物之一(例如StringInteger ,但也有一些我自己的类型)。 我正在使用GSON来解析这些模型的JSON表示。

我的问题是我正在处理的服务器(这是我无法控制的)以某种方式删除了单例数组并用包含的对象替换它们。

例如,而不是返回:

 { "foo": [ "bar"], "bleh": [ { "some": "object" } ] } 

它返回:

 { "foo": "bar", "bleh": { "some": "object" } } 

现在假设Java模型类看起来像这样:

 public class Model { private List foo; private List bleh; } 

目前这会导致GSON抛出exception,因为它找到了BEGIN_STRINGBEGIN_OBJECT ,它预期BEGIN_ARRAY

对于数组或字符串列表,可以使用TypeAdapter<List>轻松解决。 但问题是我有许多不同元素类型的List ,我不想为每种情况编写单独的TypeAdapter 。 我也没有能够使用通用的TypeAdapter<List> ,因为在某些时候你需要知道类型。 那么还有另一种方法可以将GSON配置为足够聪明以将单个对象或值转换为数组/列表吗? 或者换句话说,只是“假装” [并且]在那里它预期找到它们虽然它们不在那里?

但问题是我有许多不同元素类型的列表,我不想为每种情况编写单独的TypeAdapter。 我也没能使用通用的TypeAdapter>,因为在某些时候你需要知道类型。

这是适配器工厂的类型:您可以控制Gson实例配置中的每个类型。

 final class AlwaysListTypeAdapterFactory implements TypeAdapterFactory { // Gson can instantiate it itself private AlwaysListTypeAdapterFactory() { } @Override public  TypeAdapter create(final Gson gson, final TypeToken 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 elementTypeAdapter = (TypeAdapter) 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 alwaysListTypeAdapter = (TypeAdapter) 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 extends TypeAdapter> { private final TypeAdapter elementTypeAdapter; private AlwaysListTypeAdapter(final TypeAdapter elementTypeAdapter) { this.elementTypeAdapter = elementTypeAdapter; } @Override public void write(final JsonWriter out, final List list) { throw new UnsupportedOperationException(); } @Override public List read(final JsonReader in) throws IOException { // This is where we detect the list "type" final List list = new ArrayList<>(); final JsonToken token = in.peek(); switch ( token ) { case BEGIN_ARRAY: // If it's a regular list, just consume [, , 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注释可以更精确:

 final class Model { @JsonAdapter(AlwaysListTypeAdapterFactory.class) final List foo = null; @JsonAdapter(AlwaysListTypeAdapterFactory.class) final List 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 + '\'' + '}'; } } 

测试数据:

single.json

 { "foo": "bar", "bleh": {"some": "object"} } 

list.json

 { "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’}]}

你可以简单地编写自己的JsonDeserializer来检查你的blehfoo是JsonObjects还是JsonArrays。

要检查JsonElement是数组还是对象:

 JsonElement element = ...; if (element.isJsonObject()) { //element is a JsonObject } else if (element.isJsonArray()) { //element is a JsonArray } 

使用GSON库时,您只需检查以下标记是对象还是数组。 这当然要求您在解析XML时更精细,但它允许您完全控制您希望从中获取什么。 有时我们不受XML的控制,它可能会派上用场。

这是一个使用JsonReader类来解析文件来检查下一个标记是对象还是数组的JsonReader

 if (jsonReader.peek() == JsonToken.BEGIN_ARRAY) { jsonReader.beginArray() } else if (jsonReader.peek() == JsonToken.BEGIN_OBJECT) { jsonReader.beginObject() } 

在数组/对象的末尾,您可以执行相同的操作,但是对于结束标记:

 if (jsonReader.peek() == JsonToken.END_ARRAY) { jsonReader.endArray() } else if (jsonReader.peek() == JsonToken.END_OBJECT) { jsonReader.endObject() } 

这样,您可以使用相同的代码(添加额外的检查,以validation您是在数组上还是在对象上)来解析对象数组或单个对象。