Gson: indexed object to list Gson: indexed object to list json json

Gson: indexed object to list


It's pretty easy in Gson.

public final class MapToListTypeAdapterFactory        implements TypeAdapterFactory {    private MapToListTypeAdapterFactory() {    }    @Override    @Nullable    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {        if ( !List.class.isAssignableFrom(typeToken.getRawType()) ) {            return null;        }        final Type elementType = typeToken.getType() instanceof ParameterizedType                ? ((ParameterizedType) typeToken.getType()).getActualTypeArguments()[0]                : Object.class;        final TypeAdapter<?> elementTypeAdapter = gson.getAdapter(TypeToken.get(elementType));        final TypeAdapter<List<Object>> listTypeAdapter = new TypeAdapter<List<Object>>() {            @Override            public void write(final JsonWriter out, final List<Object> value) {                throw new UnsupportedOperationException();            }            @Override            public List<Object> read(final JsonReader in)                    throws IOException {                in.beginObject();                final ArrayList<Object> list = new ArrayList<>();                while ( in.hasNext() ) {                    final int index = Integer.parseInt(in.nextName());                    final Object element = elementTypeAdapter.read(in);                    ensureSize(list, index + 1);                    list.set(index, element);                }                in.endObject();                return list;            }        };        @SuppressWarnings("unchecked")        final TypeAdapter<T> typeAdapter = (TypeAdapter<T>) listTypeAdapter                .nullSafe();        return typeAdapter;    }    // https://stackoverflow.com/questions/7688151/java-arraylist-ensurecapacity-not-working/7688171#7688171    private static void ensureSize(final ArrayList<?> list, final int size) {        list.ensureCapacity(size);        while ( list.size() < size ) {            list.add(null);        }    }}

The type adapter factory above does the following things:

  • checks if the target type is List (it does not really work with linked lists but it's fine for simplification);
  • extracts the type parameter of the target list, hence its elements type and resolves a corresponding type adapter;
  • substitutes the original type adapter with a map-reading one that reads map keys assuming them as the new list indices (and enlarges the result list if necessary) and reads every map value using the original element type adapter. Note that the assumption that the list may have sparse indices is vulnerable and I only put it for demo purposes (say, the input JSON declares a single-element map with the only index "999999" that enlarges the list dramatically), so you can ignore the index value using the add(element) method only.

Simply annotate the pointData field in the Canvas class with @JsonAdapter(MapToListTypeAdapterFactory.class) and it will work.


I'm affraid your POJO classes needs to be like

class Canvas {  int width;  int height;  Map<String, PointData> pointData;  ...}class PointData {  int x;  int y;  ...}

and you can get rid of PointDataKeyedList and KeyedListDeserializer

public static void main(String[] args) {        String json = "{\n" +                "  \"width\": 10,\n" +                "  \"height\": 20,\n" +                "  \"pointData\": {\n" +                "    \"0\": {\n" +                "      \"x\": 7,\n" +                "      \"y\": 5\n" +                "    },\n" +                "    \"1\": {\n" +                "      \"x\": 4,\n" +                "      \"y\": 20\n" +                "    },\n" +                "    \"2\": {\n" +                "      \"x\": 1,\n" +                "      \"y\": 3\n" +                "    }\n" +                "  }\n" +                "}";        System.out.println(json);        Gson gson = new Gson();        Canvas canvas = gson.fromJson(json, Canvas.class);        System.out.println(canvas);}

enter image description here