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

问题描述

我见过很多使用自定义TypeAdapter的简单例子。最有帮助的是。但是,这还没有回答我的问题。



我想定制对象中单个字段的序列化,并让默认的Gson机制负责其余部分。

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

  public class MyClass extends SomeClass {

@Expose private HashMap< String,MyObject1>名单;
@Expose private HashMap< String,MyObject2>来源;
私有LinkedHashMap< String,SomeClass> customSerializeThis;
[snip]
}


解决方案

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



首先,写一个摘要 TypeAdapterFactory ,它使您可以修改传出数据。这个例子在Gson 2.2中使用了一个名为 getDelegateAdapter()的新API,允许你查找默认使用Gson的适配器。如果您只是想调整标准行为,代理适配器非常方便。与完整的自定义类型适配器不同,它们会在您添加和删除字段时自动保持最新。

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

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

@SuppressWarnings(unchecked)//我们使用运行时检查来保证'C'和'T'相等
public final< T> TypeAdapter< T> create(Gson gson,TypeToken< T> type){
return type.getRawType()== customizedClass
? (TypeAdapter< T>)customizeMyClassAdapter(gson,(TypeToken< C>)类型)
:null;
}

私人TypeAdapter< C> customizeMyClassAdapter(Gson gson,TypeToken< C>类型){
final TypeAdapter< C> delegate = gson.getDelegateAdapter(this,type);
final TypeAdapter< JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
返回新的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);
返回delegate.fromJsonTree(tree);
}
};
}

/ **
*在写入
*输出的JSON流之前,用{@code toSerialize}覆盖它。
* /
protected void beforeWrite(C source,JsonElement toSerialize){
}
$ b $ / **
*用代码覆盖反序列化},然后将其解析为
*应用程序类型。
* /
protected void afterRead(JsonElement反序列化){
}
}

上面的类使用默认序列化来获取JSON树(由 JsonElement 表示),然后调用钩子方法 beforeWrite()允许子类自定义该树。类似地,对于使用 afterRead()



的反序列化,接下来我们将子类用于特定的 MyClass 例子。为了说明,我将在序列化时向地图添加一个名为'size'的合成属性。而对于对称性,我将在反序列化时将其删除。在实践中,这可以是任何定制。

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

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

$ b @Override protected void afterRead(JsonElement deserialized){
JsonObject custom = deserialized.getAsJsonObject()。get(custom)。getAsJsonObject();
custom.remove(size);






最后把它们放在一起,创建一个自定义的<$

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

Gson的新和类型功能非常强大,但它们也是抽象的并且可以有效地使用。希望你找到这个例子有用!


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.

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

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.

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) {
  }
}

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().

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");
  }
}

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'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!

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

10-19 01:52