让GSON在需要阵列的地方接受单个对象 [英] Make GSON accept single objects where it expects arrays

查看:78
本文介绍了让GSON在需要阵列的地方接受单个对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一堆模型类,它们的字段类型为 List< X> 其中 X (例如 String Integer ,但也包括我自己的一些类型)。我使用GSON来解析这些模型的JSON代表。我的问题是,我处理的服务器(这是我无法控制的)以某种方式删除了单例数组,并用包含的对象替换它们。
例如,不是返回:

I have bunch of model classes which have fields of type List<X> where X is one of many things (e.g. String, Integer, but also some of my own types). I'm using GSON to parse JSON represenations of these models. My problem is that the server I'm deal with (which is beyond my control) somehow removed singleton arrays and replaces them by the contained object. For example, instead of returning:

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





It returns:

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

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

Now assume that the Java model class look something like this:

public class Model {
   private List<String> foo;
   private List<SomeObject> bleh;
}

目前这会导致GSON抛出异常,因为它找到 BEGIN_STRING BEGIN_OBJECT 预计 BEGIN_ARRAY

Currently this causes GSON to throw an exception because it finds BEGIN_STRING or BEGIN_OBJECT where it expects BEGIN_ARRAY.

对于数组或字符串列表,可以使用 TypeAdapter< List< String>> 轻松解决。但问题是我有 List s有很多不同的元素类型,我不想编写单独的 TypeAdapter 针对每种情况。我也无法使用通用的 TypeAdapter< List<>> ,因为在某些时候您需要知道类型。
那么还有另外一种方法来配置GSON足够聪明,将单个对象或值转换为数组/列表?或者换句话说,只是假装 [] 存在于它希望找到它们的位置,但却没有't?

For arrays or lists of Strings this is easily solved using a TypeAdapter<List<String>>. But the problem is I have Lists with many different element types and I don't want to write a separate TypeAdapter for each case. Nor have I been able to a generic TypeAdapter<List<?>>, because at some point you need to know the type. So is there another way to configure GSON to be smart enough to turn single objects or values into arrays/lists? Or in other words, just "pretend" that the [ and ] are there where it expects to find them but doesn't?

推荐答案


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

But the problem is I have Lists with many different element types and I don't want to write a separate TypeAdapter for each case. Nor have I been able to a generic TypeAdapter>, because at some point you need to know the type.

这是什么类型的适配器工厂的设计目的是:您可以控制 Gson 实例配置中的每一种类型。
$ b

This is what type adapter factories are designed for: you can control every type in Gson instance configuration.

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 code> annotation:
$ b

Now you just have to tell Gson which fields are not well-formed. Of course, you might configure the whole Gson instance to accept such lists, but let it be more precise using the @JsonAdapter annotation:

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"}
}



< h3> list.json

list.json

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

示例: / p>

Example:

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);
        }
    }
}

p>

And the output:


Model {foo = [bar],bleh = [SomeObject {some ='object'}]}

Model { foo = [bar],bleh = [SomeObject {some ='object'}]}

Model{foo=[bar], bleh=[SomeObject{some='object'}]}
Model{foo=[bar], bleh=[SomeObject{some='object'}]}

这篇关于让GSON在需要阵列的地方接受单个对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆