簡體   English   中英

使用 TypeAdapter 的對象中一個變量(多個)的 Gson 自定義序列化器

[英]Gson custom seralizer for one variable (of many) in an object using TypeAdapter

我見過很多使用自定義 TypeAdapter 的簡單示例。 最有用的是Class TypeAdapter<T> 但這還沒有回答我的問題。

我想自定義對象中單個字段的序列化,讓默認的 Gson 機制來處理剩下的事情。

出於討論的目的,我們可以使用這個類定義作為我希望序列化的對象的類。 我想讓 Gson 序列化前兩個類成員以及基類的所有公開成員,並且我想對下面顯示的第三個也是最后一個類成員進行自定義序列化。

public class MyClass extends SomeClass {

@Expose private HashMap<String, MyObject1> lists;
@Expose private HashMap<String, MyObject2> sources;
private LinkedHashMap<String, SomeClass> customSerializeThis;
    [snip]
}

這是一個很好的問題,因為它隔離了一些應該很容易但實際上需要大量代碼的東西。

首先,編寫一個抽象的TypeAdapterFactory ,它為您提供修改傳出數據的鈎子。 此示例使用 Gson 2.2 中名為getDelegateAdapter()的新 API,它允許您查找 Gson 默認使用的適配器。 如果您只想調整標准行為,委托適配器非常方便。 與完全自定義類型的適配器不同,它們會在您添加和刪除字段時自動保持最新狀態。

public abstract class CustomizedTypeAdapterFactory<C>
    implements TypeAdapterFactory {
  private final Class<C> customizedClass;

  public CustomizedTypeAdapterFactory(Class<C> customizedClass) {
    this.customizedClass = customizedClass;
  }

  @SuppressWarnings("unchecked") // we use a runtime check to guarantee that 'C' and 'T' are equal
  public final <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
    return type.getRawType() == customizedClass
        ? (TypeAdapter<T>) customizeMyClassAdapter(gson, (TypeToken<C>) type)
        : null;
  }

  private TypeAdapter<C> customizeMyClassAdapter(Gson gson, TypeToken<C> type) {
    final TypeAdapter<C> delegate = gson.getDelegateAdapter(this, type);
    final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
    return new TypeAdapter<C>() {
      @Override public void write(JsonWriter out, C value) throws IOException {
        JsonElement tree = delegate.toJsonTree(value);
        beforeWrite(value, tree);
        elementAdapter.write(out, tree);
      }
      @Override public C read(JsonReader in) throws IOException {
        JsonElement tree = elementAdapter.read(in);
        afterRead(tree);
        return delegate.fromJsonTree(tree);
      }
    };
  }

  /**
   * Override this to muck with {@code toSerialize} before it is written to
   * the outgoing JSON stream.
   */
  protected void beforeWrite(C source, JsonElement toSerialize) {
  }

  /**
   * Override this to muck with {@code deserialized} before it parsed into
   * the application type.
   */
  protected void afterRead(JsonElement deserialized) {
  }
}

上面的類使用默認序列化來獲取一個 JSON 樹(由JsonElement表示),然后調用鈎子方法beforeWrite()以允許子類自定義該樹。 afterRead()反序列化類似。

接下來,我們為特定的MyClass示例將其子類化。 為了說明,我將在地圖序列化時向地圖添加一個名為“大小”的綜合屬性。 為了對稱,我會在反序列化時將其刪除。 在實踐中,這可以是任何定制。

private class MyClassTypeAdapterFactory extends CustomizedTypeAdapterFactory<MyClass> {
  private MyClassTypeAdapterFactory() {
    super(MyClass.class);
  }

  @Override protected void beforeWrite(MyClass source, JsonElement toSerialize) {
    JsonObject custom = toSerialize.getAsJsonObject().get("custom").getAsJsonObject();
    custom.add("size", new JsonPrimitive(custom.entrySet().size()));
  }

  @Override protected void afterRead(JsonElement deserialized) {
    JsonObject custom = deserialized.getAsJsonObject().get("custom").getAsJsonObject();
    custom.remove("size");
  }
}

最后通過創建一個使用新類型適配器的自定義Gson實例將它們放在一起:

Gson gson = new GsonBuilder()
    .registerTypeAdapterFactory(new MyClassTypeAdapterFactory())
    .create();

Gson 新的TypeAdapterTypeAdapterFactory類型非常強大,但它們也是抽象的,需要實踐才能有效地使用。 希望你覺得這個例子很有用!

還有另一種方法。 正如 Jesse Wilson 所說,這應該很容易。 猜猜看,這容易!

如果您為您的類型實現JsonSerializerJsonDeserializer ,您可以處理您想要的部分並將其他所有內容委托給 Gson ,只需很少的代碼。 為方便起見,我在下面的另一個問題中引用了@Perception 的回答,有關詳細信息,請參閱該答案:

在這種情況下,最好使用JsonSerializer而不是TypeAdapter ,原因很簡單,序列化程序可以訪問其序列化上下文。

 public class PairSerializer implements JsonSerializer<Pair> { @Override public JsonElement serialize(final Pair value, final Type type, final JsonSerializationContext context) { final JsonObject jsonObj = new JsonObject(); jsonObj.add("first", context.serialize(value.getFirst())); jsonObj.add("second", context.serialize(value.getSecond())); return jsonObj; } }

這樣做的主要優點(除了避免復雜的解決方法)是您仍然可以利用可能已在主上下文中注冊的其他類型適配器和自定義序列化程序。 請注意,序列化程序和適配器的注冊使用完全相同的代碼。

但是,我承認如果您經常修改 Java 對象中的字段,Jesse 的方法看起來會更好。 這是易用性與靈活性之間的權衡,任您選擇。

我同事也提到了@JsonAdapter注解的使用

https://google.github.io/gson/apidocs/com/google/gson/annotations/JsonAdapter.html

該頁面已移至此處: https : //www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/annotations/JsonAdapter.html

例子:

 private static final class Gadget {
   @JsonAdapter(UserJsonAdapter2.class)
   final User user;
   Gadget(User user) {
       this.user = user;
   }
 }

暫無
暫無

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

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