Страницы

Поиск по вопросам

понедельник, 16 декабря 2019 г.

Обобщенный JSON

#java #json #gson


Сервер присылает ответы в формате

{response: {.......}}


например

{response: {code: 0}}
{response: {type: "test"}}


Пытаюсь сделать парсинг при помощи библиотеки gson в таком виде

class Response {
    private T response;
    public T getResponse() {
        return response;
    }
}

class CodeInfo {
    private int code;
}

class TypeInfo {
    private String type;
}

 T parse(String str) {
  Response resp = gson.fromJson(str, Response.class);
  return resp.getResponse();
}

CodeInfo = parse("{response: {code: 0}}");


Внутренние классы довольно обширные

На конструкции Response.class компилятор меня посылает. Решается ли задача без
кучи наследников Response? В принципе, могу даже сменить парсер
    


Ответы

Ответ 1



Спасибо @diraria за направление на путь истинный. Итого получилось public T parse(String str, Class AClass) { Response resp = JSON.fromJson( str, TypeToken.getParameterized(Response.class, AClass).getType() ); return resp.getResponse(); } CodeInfo info = parse("{response: {code: 0}}", CodeInfo.class) Если писать так public T parse(String str) { Response resp = JSON.fromJson( str, new TypeToken>(){}.getType() ); return resp.getResponse(); } то строка парсится, но resp.getResponse() возвращает объект класса com.google.gson.internal.LinkedTreeMap

Ответ 2



public T parse(String str) возвращает LinkedTreeMap, потому что T существует буквально только в воображении компилятора, и поэтому во время исполнения Gson не имеет достаточно информации о типе (заметьте, я сказал "тип", а не "класс"), которым параметризируется Response. У public T parse(String str, Class AClass) тоже проблемы, потому что объекты java.lang.Class не содержат информации о параметризации (так, впрочем, реализованы обобщённые типы в Java). Вследствие чего Response.class просто не имеет смысла (Class предоставляет информацию только о загруженном классе или о прититивном типе!), и поэтому компилятор ожидает Response.class (хотя, я думаю, что в Java можно было бы ввести что-то типа Response.type, определявший объект ParameterizedType; если я не ошибаюсь, аналогичная возможность есть в Kotlin). ParameterizedType, используемый Gson-ом на всю катушку, позволяет сохранять информацию о параметризации типа. Т.е., скорее нужно иметь следующую реализацию: private static T parse(final String json, final java.lang.reflect.Type type) { final Response response = gson.fromJson(json, TypeToken.getParameterized(Response.class, type).getType()); return response.response; } В этом случае, появляется возможность передавать в метод тип со сколь угодно сложной параметеризацией -- достаточно реализации интерфейса. Я, правда, уже не помню, есть ли в Gson готовые методы, позволяющие конструировать информацию о типах, не используя свои реализации. Но сам по себе Type не имеет сильного контроля типов, что чревато неприятными ошибками во время исполнения (например, ParameterizedType.getActualTypeArguments, возвращающий примитивный тип или неоответствие между типом переменной-приёмником и типом, описуемым ParameterizedType). Во избежение неприятностей и для ужесточения контроля типов в Gson имеется TypeToken (имеет аналоги в Google Guava, Jackson и Spring Framework под другими именами). Его задача -- содержать полную информацию о типе, определяемой ещё на этапе компиляции, что позволяет использовать компилятор для контроля типов. Сам TypeToken является абстрактным классом, заставляя в субклассах определять параметризацию, которую можно прочитать во время выполнения. И, как по мне, это позволяет весьма элегантно обходить ограничения, связанные с затиранием информации об обобщённых типах в Java: private static final TypeToken> responseCodeInfoTypeToken = new TypeToken>() { }; ... final CodeInfo codeInfo = parse("{response: {code: 1230}}", responseCodeInfoTypeToken); assert codeInfo.code == 1230; ... private static T parse(final String json, final TypeToken> typeToken) { final Response response = gson.fromJson(json, typeToken.getType()); return response.response; } TypeToken можно закешировать в статическом поле, он иммутабелен.

Комментариев нет:

Отправить комментарий