使用 TypeAdapter 的对象中一个变量(多个)的 Gson 自定义序列化器 [英] Gson custom seralizer for one variable (of many) in an object using TypeAdapter

查看:12
本文介绍了使用 TypeAdapter 的对象中一个变量(多个)的 Gson 自定义序列化器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我见过很多使用自定义 TypeAdapter 的简单示例.最有帮助的是 Class TypeAdapter<T>.但这还没有回答我的问题.

I've seen plenty of simple examples of using a custom TypeAdapter. The most helpful has been Class TypeAdapter<T>. But that hasn't answered my question yet.

我想自定义对象中单个字段的序列化,让默认的Gson机制来处理剩下的事情.

I want to customize the serialization of a single field in the object and let the default Gson mechanism take care of the rest.

出于讨论的目的,我们可以使用这个类定义作为我希望序列化的对象的类.我想让 Gson 序列化前两个类成员以及基类的所有公开成员,并且我想对下面显示的第 3 个和最后一个类成员进行自定义序列化.

For discussion purposes, we can use this class definition as the class of the object I wish to serialize. I want to let Gson serialize the first two class members as well as all exposed members of the base class, and I want to do custom serialization for the 3rd and final class member shown below.

public class MyClass extends SomeClass {

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

推荐答案

这是一个很好的问题,因为它隔离了一些应该很容易但实际上需要大量代码的东西.

This is a great question because it isolates something that should be easy but actually requires a lot of code.

首先,编写一个抽象的TypeAdapterFactory,它为您提供修改传出数据的钩子.此示例使用 Gson 2.2 中名为 getDelegateAdapter() 的新 API,它允许您查找 Gson 默认使用的适配器.如果您只想调整标准行为,委托适配器非常方便.与完全自定义类型的适配器不同,它们会在您添加和删除字段时自动保持最新状态.

To start off, write an abstract TypeAdapterFactory that gives you hooks to modify the outgoing data. This example uses a new API in Gson 2.2 called getDelegateAdapter() that allows you to look up the adapter that Gson would use by default. The delegate adapters are extremely handy if you just want to tweak the standard behavior. And unlike full custom type adapters, they'll stay up-to-date automatically as you add and remove fields.

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() 反序列化类似.

The above class uses the default serialization to get a JSON tree (represented by JsonElement), and then calls the hook method beforeWrite() to allow the subclass to customize that tree. Similarly for deserialization with afterRead().

接下来,我们为特定的 MyClass 示例将其子类化.为了说明,我将在地图序列化时向地图添加一个名为大小"的综合属性.为了对称,我会在反序列化时将其删除.在实践中,这可以是任何自定义.

Next we subclass this for the specific MyClass example. To illustrate I'll add a synthetic property called 'size' to the map when it's serialized. And for symmetry I'll remove it when it's deserialized. In practice this could be any customization.

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 实例将它们组合在一起:

Finally put it all together by creating a customized Gson instance that uses the new type adapter:

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

Gson 的新 TypeAdapterTypeAdapterFactory 类型非常强大,但它们也是抽象的,需要练习才能有效地使用.希望你觉得这个例子很有用!

Gson's new TypeAdapter and TypeAdapterFactory types are extremely powerful, but they're also abstract and take practice to use effectively. Hopefully you find this example useful!

这篇关于使用 TypeAdapter 的对象中一个变量(多个)的 Gson 自定义序列化器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆