簡體   English   中英

Gson:如何更改序列化中的特定字段鍵

[英]Gson: How to change specific fields key from serialization

我正在使用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;
    }     

}

我必須根據資源動態更改字段名稱。 有什么辦法可以改變嗎?

使用地圖可能不是最佳選擇,因為您的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式”的解決方案是創建一種特殊類型的適配器,以免在需要時不重新映射響應對象。

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 ,以讓Gson使用其自己的(反)序列化策略。

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”:“ STATUS”,“ message”:“ MESSAGE”,“ Response”:[“ foo”,“ bar”]}

您可能要考慮的另一個選項是:

  • 如果應該對名稱進行硬編碼,則使Response類成為抽象類,並讓子類通過@SerializedName定義自己的data字段名稱。
  • 創建實現ReflectiveTypeAdapterFactory (請參閱Gson源代碼),並使字段名稱動態化,而無需創建中間JSON樹。

如果您需要更改字段名稱,則意味着您不需要類型安全,因此可以執行以下操作:

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

您仍然可以在此低級代碼之上包裝一個便捷庫,以解決常見的用例。

在這種情況下為什么不使用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);
}

請注意,數據字段也可以是<String, Object><Long, Object>的HashMap,以保存子對象,從而接受所有Gson支持的結構。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM