【问题标题】:How to have a single GSON custom serializer to apply to all subclasses?如何让单个 GSON 自定义序列化程序应用于所有子类?
【发布时间】:2018-10-12 07:21:56
【问题描述】:

我正在使用 GSON 将通用序列化程序应用于抽象 Base 类的所有子类。但是,当给定 Base 类的实际子类时,GSON 不会调用我的序列化程序,除非明确告知使用 Base.class 作为强制转换。这是我所说的一个简单实例。

public interface Base<T>{
  String getName();
  public List<Object> getChildren();
}

public class Derived1 implements Base<Integer>{
  private Integer x = 5; 
  String getName(){ 
    return "Name: " + x;
  }
  List<Object> getChildren(){
    return Lists.newArrayList(new Derived2(), "Some string");
  }
}

public class Derived2 implements Base<Double>{
  private Double x = 6.3;
  String getName(){
    return "Name: " + x;
  }
  List<Object> getChildren(){
    return new List<>();
  }
}

我正在按如下方式创建序列化程序:

JsonSerializer customAdapter = new JsonSerializer<Base>(){
  @Override
  JsonElement serialize(Base base, Type sourceType, JsonSerializationContext context){
    JsonObject jsonObject = new JsonObject();
    jsonObject.addProperty("name", base.getName());
    JsonArray jsonArray = new JsonArray();
    for (Object child : base.getChildren()){
      jsonArray.add(context.serialize(child));
    }
    if (jsonArray.size() != 0){
      jsonObject.add("children", jsonArray);
    }
  }
};

Gson customSerializer = new GsonBuilder()
  .registerTypeAdapter(Base.class, customAdapter)
  .create();

但是,将我的自定义序列化程序应用于List 的子类并没有达到预期的效果。

customSerializer.toJson(Lists.newArrayList(new Derived1(), new Derived2()));

这会将默认的 GSON 序列化应用于我的子类。有什么简单的方法可以让我的自定义序列化程序在父类的所有子类上使用我的自定义适配器?我怀疑一种解决方案是使用反射来遍历Base 的所有子类并注册自定义适配器,但如果可能的话,我想避免这样的事情。

注意:我现在不关心反序列化。

【问题讨论】:

  • 不要使用 JsonSerializer&lt;Base&gt; 只是 JsonSerializer 并在覆盖方法中使用 Object 并检查并强制转换为 Base
  • 这没有任何作用。

标签: java json serialization gson


【解决方案1】:

也许你不应该使用JsonSerializer。也就是说,如果您使用 TypeAdapter 通过注册 TypeAdapterFactory 来告诉 Gson 如何序列化任何类来执行相同的魔法,这是可能的。

请参阅下面的TypeAdapterFactoryTypeAdapter

public class CustomAdapterFactory implements TypeAdapterFactory {

    @SuppressWarnings("unchecked")
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
        // If the class that type token represents is a subclass of Base
        // then return your special adapter 
        if(Base.class.isAssignableFrom(typeToken.getRawType())) {
            return (TypeAdapter<T>) customTypeAdapter;          
        }
        return null;
    }

    private TypeAdapter<Base<?>> customTypeAdapter = new TypeAdapter<Base<?>>() {
        @Override
        public void write(JsonWriter out, Base<?> value) throws IOException {
            out.beginObject();
            out.value(value.getName());
            out.endObject();            
        }

        @Override
        public Base<?> read(JsonReader in) throws IOException {
            // Deserializing to subclasses not interesting yet.
            // Actually it is impossible if the JSON does not contain 
            // information about the subclass to which to deserialize
            return null;
        }

    };
}

如果你这样做:

@Slf4j
public class SubClassTest {
    @Test
    public void testIt() {
        Gson gson = new GsonBuilder()
                .setPrettyPrinting()
                .registerTypeAdapterFactory(new CustomAdapterFactory())
                .create();
        log.info("\n{}", gson.toJson(new Derived1()));
        log.info("\n{}", gson.toJson(new Derived2()));
    }
}

输出将是这样的:

2018-10-12 23:13:17.037 信息 org.example.gson.subclass.SubClassTest:19 - {“名称”:“名称:5”} 2018-10-12 23:13:17.043 信息 org.example.gson.subclass.SubClassTest:20 - {“名称”:“名称:6.3” }

如果它不是您想要的,只需修复 customTypeAdapter 中的 write(..) 方法即可。

【讨论】:

  • 就可继承性而言,这听起来不错。但是,如果可能,我想执行从我的 Java 对象到 GSON 树的中间转换。此外,我的真实应用程序对象具有更深层次的结构(例如,Base 中的Base 对象列表),因此我希望能够在Base 的成员上递归调用相同的序列化程序。调整此策略以适应这两个约束是否容易?
  • @Navneeth 可能是,但您可以使用 Base-in-Base 类结构示例和生成的 JSON 来更新您的问题吗?然后我会尝试更新或给出另一个答案。
  • 我让它更能代表一般情况。子节点存在于一种通用数据结构中,可能包括Bases 和其他对象。
猜你喜欢
  • 1970-01-01
  • 2013-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-16
  • 2011-08-28
  • 2013-09-09
  • 2013-03-09
相关资源
最近更新 更多