AutoValue Gson adapter for map <String, List <Obj>>

I am trying to find a way to use AutoValue to deserialize a JSON OBJ to a Java class (which is also Parcelable)

The JSON response usually has the form:

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

where varKey1and varKey2are strings that are not fixed / predetermined, therefore they can have any value.

It’s hard for me to understand what the type of Adapter should look like for this, although with AutoValue Gson, and any help with that would be greatly appreciated.

+4
source share
1 answer

, AutoValue AutoValue: Gson Extension, Map<String, List<Obj>> JSON, , . , AutoValue Gson TypeAdapterFactory.

JSON Map<String, List<Obj>>, :

  • ... Map<String, Wrapper>, - List<Obj> ( @AutoValue -) , Wrapper.
  • ... , AutoValue: Gson.

, , , 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>>() {
    };

}

factory

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

}

, factory , JSON. - factory, null Gson ( ).

, Auto Value: Gson Extension, , . , , Gson, , , Auto Value: Gson Extension.

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

}

factory

factory, 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 = > [Obj {name = name1, value = val1}, Obj {name = name2, value = val2}]
varKey2 = > [Obj {name = name1, value = val1}, Obj {name = name2, value = val2}]

, :

varKey1 = > Wrapper { = [Obj {name = name1, value = val1}, Obj {name = name2, value = val2}]}
varKey2 = > Wrapper { = [Obj {name = name1, value = val1}, Obj {name = name2, value = val2}]}

+2

Source: https://habr.com/ru/post/1667809/


All Articles