2017-02-18 7 views
0

Я использую Gson для сериализации, и я изо всех сил пытаюсь изменить имена полей. Вот мой класс:Gson: Как изменить определенные поля из сериализации

public class Response<T> 
{ 

    private String status; 
    private String message; 
    private T data; 

    public Response(T data) 
    { 
     this.setData(data); 
    } 

    public String getStatus() { 
     return status; 
    } 

    public void setStatus(String 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; 
    }  

} 

Мне нужно динамически менять имена полей на основе ресурса. Есть ли способ изменить это?

ответ

0

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

Map<String, Object> response = new LinkedHashMap<>(); 
response.put("message", message); 
response.put("status", status); 
response.put(dynamicFieldName, dynamicFieldValue); 
String json = gson.toJson(response); 

Вы все еще можете обернуть библиотеку удобства поверх этого низкого уровня код для учета случаев общего использования.

0

Почему бы не использовать HashMap для таких случаев?

private HashMap<String, String> data; 

public HashMap<String, String> getData() { 
    return this.data; 
} 

public void setDataValue(String key, String value) { 
    data.put(key, value); 
} 

Обратите внимание, что поле данных может быть HashMap из <String, Object> или <Long, Object> тоже, чтобы сохранить вложенные объекты, таким образом, принимая все виды Gson поддерживаются структуры.

1

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