Intereting Posts
Фильтр сканирования Bluetooth LE не работает OptionsMenu вложенных фрагментов в ViewPager Анимация при изменении LayoutParams в LinearLayout Android AsyncTask остается в рабочем состоянии после завершения Android: как заставить активность возвращать результаты деятельности, которая ее вызывает? OnClickListener не запускается для пользовательского представления Android: отображение цифр в арабском формате Как работает синтаксис доступа к свойствам Kotlin для классов Java? Эмулятор Android Studio не поставляется с Play Store для API 23 Как я могу снять или сбросить переключатель? Как имитировать убийство приложения Android GC Есть ли способ вставить ImageSpan в TextView без нарушения текста? Самый простой способ узнать (программно), какая папка ресурсов используется на основе плотности / размера экрана Где найти информацию о команде оболочки «service call» для Android? Храните камеру libgdx внутри границ при панорамировании и масштабировании

Модифицировать gson-конвертер для вложенных json с разными объектами

Я имею структуру JSON, как следует –

{ "status": true, "message": "Registration Complete.", "data": { "user": { "username": "user88", "email": "user@domain.com", "created_on": "1426171225", "last_login": null, "active": "1", "first_name": "User", "last_name": "", "company": null, "phone": null, "sign_up_mode": "GOOGLE_PLUS" } } } 

Выше формат является общим. Только ключ data может содержать различные типы информации, такие как user , product , invoice и т. Д.

Я хочу сохранить status , message и ключи data одинаково в каждом ответе на отдых. data будут обрабатываться в соответствии со status и message будет отображаться пользователю.

Так что в принципе, выше формат желательно во всех apis. Только информация внутри ключа data будет отличаться каждый раз.

И я настроил следующий класс и настроил его как gson converter – MyResponse.java

 public class MyResponse<T> implements Serializable{ private boolean status ; private String message ; private T data; public boolean isStatus() { return status; } public void setStatus(boolean status) { this.status = status; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } 

Deserializer.java

 class Deserializer<T> implements JsonDeserializer<T>{ @Override public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException{ JsonElement content = je.getAsJsonObject(); // Deserialize it. You use a new instance of Gson to avoid infinite recursion to this deserializer return new Gson().fromJson(content, type); } } 

И использовал его следующим образом:

 GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES); gsonBuilder.registerTypeAdapter(MyResponse.class, new Deserializer<MyResponse>()); ...... ..... .... restBuilder.setConverter(new GsonConverter(gsonBuilder.create())); 

Сервисный интерфейс выглядит следующим образом:

 @POST("/register") public void test1(@Body MeUser meUser, Callback<MyResponse<MeUser>> apiResponseCallback); @POST("/other") public void test2(Callback<MyResponse<Product>> apiResponseCallback); 

проблема

Я могу получить доступ к полям status и message из внутреннего обратного вызова. Но информация внутри ключа data не анализируется, и модель, подобная MeUser и Product всегда возвращается как пустая.

Если я изменю структуру json на следующий выше код работает отлично –

 { "status": true, "message": "Registration Complete.", "data": { "username": "user88", "email": "user@domain.com", "created_on": "1426171225", "last_login": null, "active": "1", "first_name": "User", "last_name": "", "company": null, "phone": null, "sign_up_mode": "GOOGLE_PLUS" } } 

Как я могу заставить это работать с указанием отдельного ключа внутри объекта data и его синтаксического анализа?

Solutions Collecting From Web of "Модифицировать gson-конвертер для вложенных json с разными объектами"

Если я могу предложить что-то изменить в json, вам нужно добавить в новое поле, определяющее тип данных, поэтому json должен выглядеть следующим образом:

 { "status": true, "message": "Registration Complete.", "dataType" : "user", "data": { "username": "user88", "email": "user@domain.com", "created_on": "1426171225", "last_login": null, "active": "1", "first_name": "User", "last_name": "", "company": null, "phone": null, "sign_up_mode": "GOOGLE_PLUS" } } 

Класс MyResponse должен иметь новый DataType чтобы он выглядел следующим образом:

 public class MyResponse<T> implements Serializable{ private boolean status ; private String message ; private DataType dataType ; private T data; public DataType getDataType() { return dataType; } //... other getters and setters } 

DataType – это перечисление, которое определяет тип данных. Вы должны передать Data.class как параметр в конструкторе. Для всех типов данных вам необходимо создать новые классы. DataType должно выглядеть следующим образом:

 public enum DataType { @SerializedName("user") USER(MeUser.class), @SerializedName("product") Product(Product.class), //other types in the same way, the important think is that //the SerializedName value should be the same as dataType value from json ; Type type; DataType(Type type) { this.type = type; } public Type getType(){ return type; } } 

Дезарилизатор для Json должен выглядеть следующим образом:

 public class DeserializerJson implements JsonDeserializer<MyResponse> { @Override public MyResponse deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException { JsonObject content = je.getAsJsonObject(); MyResponse message = new Gson().fromJson(je, type); JsonElement data = content.get("data"); message.setData(new Gson().fromJson(data, message.getDataType().getType())); return message; } } 

И когда вы создаете RestAdapter , в строке, где вы регистрируете Deserializator, вы должны использовать это:

  .registerTypeAdapter(MyResponse.class, new DeserializerJson()) 

Другие классы (типы данных), которые вы определяете как стандартные POJO для Gson в отдельных классах.

Ваша проблема связана с тем, что атрибут data определяется как T который вы ожидаете от типов MeUser , Product и т. Д., Но на самом деле является объектом, который имеет внутренний атрибут, такой как user . Чтобы решить эту проблему, вам необходимо ввести еще один уровень классов, который имеет необходимые атрибуты user , product , invoice и т. Д. Этого можно легко достичь, используя статические внутренние классы.

 public class MeUser{ private User user; public static class User{ private String username; //add other attributes of the User class } } 

Может быть немного не по теме, но что произойдет, если внутренний объект содержит свойство Date? Мой TypeAdapter выглядит так:

  .registerTypeAdapter(Date.class, new DateDeserializer()) .registerTypeAdapter(GenericNotificationResponse.class, new NotificationDeserializer()) .setDateFormat("yyyy-MM-dd'T'HH:mm:ss") .create(); 

Но из-за разбора, который делается этим: message.setData(new Gson().fromJson(data, message.getDataType().getType()));

Он будет вызывать ошибку, когда он попытается десериализовать свойство Date. Есть ли быстрое решение для этого?

EDIT: Отметили ответ как принято, определенно 🙂 это помогло мне исправить мою проблему. Но теперь есть проблема с десериализатором даты.