Использование карт может быть не лучшим выбором, так как ваш класс Response
может иметь специальные аннотации Gson, которые будут игнорироваться после преобразования объектов ответа в карты.
Пусть следующий простой класс ответа:
final class Response<T> {
@Expose(serialize = true)
final String status = "STATUS";
@Expose(serialize = true)
final String message = "MESSAGE";
@Expose(serialize = true)
final T data;
@Expose(serialize = false, deserialize = false)
final String whatever = "WHATEVER";
Response(final T data) {
this.data = data;
}
}
Этот ответ не использует еще аннотации Gson для простоты. Специальное использование динамического поля переименования:
final Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
// ... any Gson configuration here ...
.create();
final Response<List<String>> response = new Response<>(ImmutableList.of("foo", "bar"));
final JsonElement jsonTree = gson.toJsonTree(response, stringListResponseTypeToken.getType());
final JsonObject responseJson = jsonTree.getAsJsonObject();
final JsonElement dataPropertyJson = responseJson.get("data");
responseJson.remove("data");
responseJson.add(response.getClass().getSimpleName(), dataPropertyJson);
gson.toJson(responseJson, System.out);
Обратите внимание, что главный трюк здесь является созданием промежуточного JSON дерева и подставив имя динамического свойства. К сожалению, для этого решения требуется промежуточное дерево JSON. Другое, более «решение Gson-ish» создает специальный адаптер типа, чтобы не перегруппировать объекты ответа каждый раз, когда это необходимо.
final Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
// ... any Gson configuration here ...
.registerTypeAdapterFactory(getDynamicPropertyResponseTypeAdapterFactory())
.create();
final Response<List<String>> response = new Response<>(ImmutableList.of("foo", "bar"));
gson.toJson(response, stringListResponseTypeToken.getType(), System.out);
Где типа адаптер завод и типа адаптеры реализованы следующим образом:
final class DynamicPropertyResponseTypeAdapterFactory
implements TypeAdapterFactory {
private static final TypeAdapterFactory dynamicPropertyResponseTypeAdapterFactory = new DynamicPropertyResponseTypeAdapterFactory();
private DynamicPropertyResponseTypeAdapterFactory() {
}
static TypeAdapterFactory getDynamicPropertyResponseTypeAdapterFactory() {
return dynamicPropertyResponseTypeAdapterFactory;
}
@Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
if (Response.class.isAssignableFrom(typeToken.getRawType())) {
@SuppressWarnings("unchecked")
final TypeAdapter<Response<Object>> delegateTypeAdapter = (TypeAdapter<Response<Object>>) gson.getDelegateAdapter(this, typeToken);
@SuppressWarnings("unchecked")
final TypeAdapter<T> castTypeAdapter = (TypeAdapter<T>) getDynamicPropertyResponseJsonTypeAdapter(delegateTypeAdapter, gson);
return castTypeAdapter;
}
return null;
}
}
Обратите внимание, что этот типа адаптер завод выбирает адаптер вниз по течению типа, чтобы избежать бесконечной рекурсии, если обрабатываются класс Response
, и в противном случае возвращается null
, чтобы использовать его собственные стратегии деривации.
final class DynamicPropertyResponseJsonTypeAdapter<T>
extends TypeAdapter<Response<T>> {
private final TypeAdapter<Response<T>> delegateTypeAdapter;
private final Gson gson;
private DynamicPropertyResponseJsonTypeAdapter(final TypeAdapter<Response<T>> delegateTypeAdapter, final Gson gson) {
this.delegateTypeAdapter = delegateTypeAdapter;
this.gson = gson;
}
static <T> TypeAdapter<Response<T>> getDynamicPropertyResponseJsonTypeAdapter(final TypeAdapter<Response<T>> delegateTypeAdapter, final Gson gson) {
return new DynamicPropertyResponseJsonTypeAdapter<>(delegateTypeAdapter, gson);
}
@Override
@SuppressWarnings("resource")
public void write(final JsonWriter out, final Response<T> response)
throws IOException {
if (response == null) {
out.nullValue();
return;
}
final JsonElement jsonTree = delegateTypeAdapter.toJsonTree(response);
final JsonObject responseJson = jsonTree.getAsJsonObject();
final JsonElement dataPropertyJson = responseJson.get("data");
responseJson.remove("data");
responseJson.add(response.getClass().getSimpleName(), dataPropertyJson);
gson.toJson(responseJson, out);
}
@Override
public Response<T> read(final JsonReader in) {
throw new UnsupportedOperationException();
}
}
То же не очень дешевый трюк используется выше, но теперь он работает как часть экземпляра Gson
.В обоих случаях выход следующим образом:
{ "Статус": "STATUS", "сообщение": "MESSAGE", "Ответ": [ "Foo", "Бар"]}
Другие опции, которые вы, возможно, захотите принять во внимание, являются:
- Создание класса аннотацию
Response
и давая дочерние классы, чтобы определить свои собственные имена data
поля через @SerializedName
, если имена должны быть жестко закодированы.
- Создание вашей реализации
ReflectiveTypeAdapterFactory
(см. Исходный код Gson) и создание динамических имен полей без создания деревьев JSON.