Как сериализовать ответ на объект с помощью Gson?

Я делаю запрос непосредственно к VK api с token

Как это: https://api.vk.com/method/groups.get?fields=photo_50&access_token=MY_TOKEN&filter=admin%2C%20editor%2C%20moder&extended=1Вот спецификация API, но я не могу сериализовать ответ на объект с помощью Gson, потому что есть ответный массив int значение:

{
    "response": [
        2,
        {
            "gid": 59295,
            "name": "Создание",
            "screen_name": "book",
            "is_closed": 0,
            "type": "group",
            "photo_50": "https://pp.userapi.com/qwvD6SPkYzo.jpg"
        },
        {
            "gid": 57150,
            "name": "Массаж",
            "screen_name": "club10450",
            "is_closed": 2,
            "type": "group",
            "photo_50": "https://pp.userapi.com/ZKnmRkS1izs.jpg"
        }
    ]
}

Как я могу сделать сериализовать его в объект с помощью Gson?

2 ответа

Решение

Решено, Добавлен параметр в URL v=5.61 номер версии

{
    "response": {
        "count": 190,
        "items": [{
            "id": 28261334,
            "name": "TJ",
            "screen_name": "tj",
            "is_closed": 0,
            "type": "page",
            "is_admin": 0,
            "is_member": 1,
            "photo_50": "https://pp.vk.me/...f2c/06crfCSL1KY.jpg"
            }]
    }
}

Несмотря на то, что вы уже решили проблему, изменив версию API с помощью параметров URL GET, здесь представлен метод работы с "нестандартными" JSON, с которыми вы можете столкнуться в будущем. Я предполагаю, что у вас есть правильные отображения, но длина массива (предположительно) указана как самый первый элемент массива. Gson не может справиться с таким особым случаем (по крайней мере, если он ожидает {...} объекты), вероятно, давая вам что-то вроде этого:

Ожидаемый BEGIN_OBJECT, но был НОМЕР в строке 3 столбца 10 пути $.response[0]

Предполагая, что у вас есть сопоставления, подобные следующим двум:

final class ElementsResponse {

    @SerializedName("response")
    final List<Element> response = null;

}
final class Element {

    @SerializedName("gid")
    final int gid = Integer.valueOf(0);

    @SerializedName("name")
    final String name = null;

    @SerializedName("screen_name")
    final String screenName = null;

    @SerializedName("is_closed")
    final int isClosed = Integer.valueOf(0);

    @SerializedName("type")
    final String type = "";

    @SerializedName("photo_50")
    final URL photo50 = null;

}

Вы можете легко создать свой адаптер типа с помощью фабрики адаптеров специального типа, чтобы иметь дело с данным JSON:

final class LengthArrayTypeAdapterFactory
        implements TypeAdapterFactory {

    // The instance holds no state and can be created as a singleton    
    private static final TypeAdapterFactory lengthArrayTypeAdapterFactory = new LengthArrayTypeAdapterFactory();

    private LengthArrayTypeAdapterFactory() {
    }

    // However, the factory method does not let a caller to create an instance itself, and _may_ create it itself if necessary (encapsulation)
    static TypeAdapterFactory getLengthArrayTypeAdapterFactory() {
        return lengthArrayTypeAdapterFactory;
    }

    @Override
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
        // Are we dealing with a java.util.List instance?
        if ( List.class.isAssignableFrom(typeToken.getRawType()) ) {
            // Resolve the list element type if possible
            final Type elementType = getElementType(typeToken.getType());
            // And request Gson for the element type adapter
            final TypeAdapter<?> elementTypeAdapter = gson.getAdapter(TypeToken.get(elementType));
            // Some Java boilerplate regarding generics in order not letting the @SuppressWarnings annotation cover too much
            @SuppressWarnings("unchecked")
            final TypeAdapter<T> castTypeAdapter = (TypeAdapter<T>) new LengthArrayTypeAdapter<>(elementTypeAdapter);
            return castTypeAdapter;
        }
        // Or let Gson pick the next downstream type adapter itself
        return null;
    }

    private static Type getElementType(final Type listType) {
        // The given type is not parameterized?
        if ( !(listType instanceof ParameterizedType) ) {
            // Probably the (de)serialized list is raw being not parameterized
            return Object.class;
        }
        final ParameterizedType parameterizedType = (ParameterizedType) listType;
        // Or just take the first type parameter (java.util.List has one type parameter only)
        return parameterizedType.getActualTypeArguments()[0];
    }

    private static final class LengthArrayTypeAdapter<E>
            extends TypeAdapter<List<E>> {

        // This type adapter is designed to read and write a single element only
        // We'll take care of all elements array ourselves
        private final TypeAdapter<E> elementTypeAdapter;

        private LengthArrayTypeAdapter(final TypeAdapter<E> elementTypeAdapter) {
            this.elementTypeAdapter = elementTypeAdapter;
        }

        @Override
        public List<E> read(final JsonReader in)
                throws IOException {
            // Gson type adapters are supposed to be null-friendly
            if ( in.peek() == NULL ) {
                return null;
            }
            // Consume the array begin token `[`
            in.beginArray();
            // The next value is most likely the array length?
            final int arrayLength = in.nextInt();
            final List<E> list = new ArrayList<>();
            // Read until the array has more elements
            while ( in.hasNext() ) {
                // And let the element type adapter read the array element so push the value to the list
                list.add(elementTypeAdapter.read(in));
            }
            // Consume the array end token `]`
            in.endArray();
            assert arrayLength == list.size();
            return list;
        }

        @Override
        @SuppressWarnings("resource")
        public void write(final JsonWriter out, final List<E> list)
                throws IOException {
            if ( list == null ) {
                // Must be null-friendly always
                out.nullValue();
            } else {
                // Writing the `[` token
                out.beginArray();
                // Writing the list size/length
                out.value(list.size());
                for ( final E element : list ) {
                    // And just write each array element
                    elementTypeAdapter.write(out, element);
                }
                // Finalizing the writing with `]`
                out.endArray();
            }
        }
    }

}

Так что все, что вам нужно было сделать, это просто добавить фабрику адаптеров типа к Gson Конфигурация создания ваших специальных массивов с учетом Gson:

final Gson gson = new GsonBuilder()
        .registerTypeAdapterFactory(getLengthArrayTypeAdapterFactory())
        .create();
final ElementsResponse elementsResponse = gson.fromJson(JSON, ElementsResponse.class);
elementsResponse.response.forEach(e -> System.out.println(e.name));
System.out.println(gson.toJson(elementsResponse));

Выход:

Создание
Массаж
{ "Ответ":[2,{"ГИД":59295,"название":"Создание","screen_name":"книга","is_closed":0,"тип": "группа", "photo_50":" https://pp.userapi.com/qwvD6SPkYzo.jpg"}, {" gid ": 57150," name ":" Массаж "," screen_name ":" club10450 "," is_closed ": 2," type ":" group "," photo_50 ":" https://pp.userapi.com/ZKnmRkS1izs.jpg"}]}

Обратите внимание, что фабрика адаптеров этого типа всегда предполагает, что первый элемент массива является числом, и вам может потребоваться проанализировать elementType если необходимо (например, если это java.lang.Number или его подкласс).

Другие вопросы по тегам