#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 за направление на путь истинный. Итого получилось publicT 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
publicT 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 можно закешировать в статическом поле, он иммутабелен.
Комментариев нет:
Отправить комментарий