用于Map <string,list <obj =“”>&gt;的AutoValue Gson类型适配器

时间:2017-01-25 20:01:11

标签: android gson auto-value

我试图找到一种方法来使用AutoValue将JSON obj反序列化为Java类(也是Parcelable)

JSON响应通常采用

的形式
{
  "varKey1": {
    "occupations": [
      {
        "value": "val1",
        "name": "name1"
      },
      {
        "value": "val2",
        "name": "name2"
      }
    ]
  },
  "varKey2": {
    "occupations": [
      {
        "value": "val1",
        "name": "name1"
      },
      {
        "value": "val2",
        "name": "name2"
      }
    ]
  }
}

其中varKey1varKey2是非固定/预定义的字符串,因此可以包含任何值。

虽然使用AutoValue Gson,我很难弄清楚这个类型的适配器应该是什么样的,并且对此的任何帮助都将非常感激。

1 个答案:

答案 0 :(得分:5)

到目前为止,我了解AutoValue和AutoValue:Gson扩展如何工作,您不能仅使用这些工具从给定的JSON反序列化Map<String, List<Obj>>,因为它们只是简单的源代码生成器。后者甚至声明为每个AutoValue带注释的对象创建简单 Gson TypeAdapterFactory。

考虑给定的JSON和Map<String, List<Obj>>,您可以:

  • ...有Map<String, Wrapper>,其中包装类包含List<Obj>(并为每个@AutoValue - 注释的)类声明一个类型适配器,因此有一个名为 Wrapper
  • ...实现AutoValue无法生成的自定义类型适配器:Gson Extension。

第二个选项听起来并不难,并且当AutoValue扩展无法为其他复杂案例生成类型适配器时,它可以帮助解决。

声明必要的类型标记

final class TypeTokens {

    private TypeTokens() {
    }

    static final TypeToken<Map<String, List<Obj>>> mapStringToObjListTypeToken = new TypeToken<Map<String, List<Obj>>>() {
    };

    static final TypeToken<List<Obj>> objListTypeTypeToken = new TypeToken<List<Obj>>() {
    };

}

实施自定义类型适配器工厂

Gson类型的适配器工厂用于解析(和绑定)特定类型的适配器,并在必要时将适配器绑定到Gson实例以处理任何Gson配置。

final class CustomTypeAdapterFactory
        implements TypeAdapterFactory {

    private static final TypeAdapterFactory customTypeAdapterFactory = new CustomTypeAdapterFactory();

    static TypeAdapterFactory getCustomTypeAdapterFactory() {
        return customTypeAdapterFactory;
    }

    @Override
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
        if ( typeToken.getType().equals(mapStringToObjListTypeToken.getType()) ) {
            @SuppressWarnings({ "unchecked", "rawtypes" })
            final TypeAdapter<T> castTypeAdapter = (TypeAdapter) getMapStringToObjectListTypeAdapter(gson);
            return castTypeAdapter;
        }
        return null;
    }

}

请注意,工厂的唯一责任是返回一个特殊类型的适配器,它可以跳过JSON中的包装器。如果向工厂请求任何其他类型,则返回null是安全的,并让Gson尝试选择另一种类型的适配器最佳匹配(内置函数或您的配置)。

实现类型适配器

这实际上是Auto Value:Gson Extension似乎无法做到的。由于类型适配器本质上是面向流的,它们可能看起来太低级,但这是Gson能做的最好的,因为流是一种非常有效的技术,它也用于自动值:Gson扩展生成的内容。

final class MapStringToObjectListTypeAdapter
        extends TypeAdapter<Map<String, List<Obj>>> {

    private final TypeAdapter<List<Obj>> wrapperAdapter;

    private MapStringToObjectListTypeAdapter(final TypeAdapter<List<Obj>> wrapperAdapter) {
        this.wrapperAdapter = wrapperAdapter;
    }

    static TypeAdapter<Map<String, List<Obj>>> getMapStringToObjectListTypeAdapter(final Gson gson) {
        return new MapStringToObjectListTypeAdapter(gson.getAdapter(objListTypeTypeToken));
    }

    @Override
    @SuppressWarnings("resource")
    public void write(final JsonWriter out, final Map<String, List<Obj>> value)
            throws IOException {
        if ( value == null ) {
            // nulls must be written
            out.nullValue();
        } else {
            out.beginObject();
            for ( final Entry<String, List<Obj>> e : value.entrySet() ) {
                out.name(e.getKey());
                out.beginObject();
                out.name("occupations");
                wrapperAdapter.write(out, e.getValue());
                out.endObject();
            }
            out.endObject();
        }
    }

    @Override
    public Map<String, List<Obj>> read(final JsonReader in)
            throws IOException {
        // if there's JSON null, then just return nothing
        if ( in.peek() == NULL ) {
            return null;
        }
        // or read the map
        final Map<String, List<Obj>> result = new LinkedHashMap<>();
        // expect the { token
        in.beginObject();
        // and read recursively until } is occurred
        while ( in.peek() != END_OBJECT ) {
            // this is the top-most level where varKey# occur
            final String key = in.nextName();
            in.beginObject();
            while ( in.peek() != END_OBJECT ) {
                final String wrapperName = in.nextName();
                switch ( wrapperName ) {
                case "occupations":
                    // if this is the "occupations" property, delegate the parsing to an underlying type adapter
                    result.put(key, wrapperAdapter.read(in));
                    break;
                default:
                    // or just skip the value (or throw an exception, up to you)
                    in.skipValue();
                    break;
                }
            }
            in.endObject();
        }
        in.endObject();
        return result;
    }

}

自动生成价值的工厂

与自定义类型适配器工厂不同,某些Gson类型的适配器工厂已经生成,并且可以处理abstract类,例如Obj(至少在您的源代码中,而不是生成的类)

@GsonTypeAdapterFactory
abstract class GeneratedTypeAdapterFactory
        implements TypeAdapterFactory {

    public static TypeAdapterFactory getGeneratedTypeAdapterFactory() {
        return new AutoValueGson_GeneratedTypeAdapterFactory();
    }

}

如何使用

private static void dump(final Map<?, ?> map) {
    for ( final Entry<?, ?> e : map.entrySet() ) {
        out.print(e.getKey());
        out.print(" => ");
        out.println(e.getValue());
    }
}

...

final Gson gson = new GsonBuilder()
        .registerTypeAdapterFactory(getGeneratedTypeAdapterFactory())
        .registerTypeAdapterFactory(getCustomTypeAdapterFactory())
        .create();
dump(gson.fromJson(json, mapStringToObjListTypeToken.getType()));

给出以下输出:

  

varKey1 =&gt; [Obj {name = name1,value = val1},Obj {name = name2,value = val2}]
  varKey2 =&gt; [Obj {name = name1,value = val1},Obj {name = name2,value = val2}]

如果仅使用第一个选项的包装器,则输出如下:

  

varKey1 =&gt; Wrapper {occupations = [Obj {name = name1,value = val1},Obj {name = name2,value = val2}]}
  varKey2 =&gt;包装{职业= [Obj {name = name1,value = val1},Obj {name = name2,value = val2}]}