使用 Gson-extras 使用瞬态变量反序列化多态 JSON

Deserializing polymorphic JSON with transient variables using Gson-extras

我有一些多态 java class,我是 serializing/deserializing。我正在使用 Gson-extras 中的 RuntimeTypeAdapterFactory 来确保 classes 序列化和反序列化正确,"type" 字段设置为派生的 class 的名称。一切正常。

我有一些其他的 classes,我是 serializing/deserializing,其中有一些应用了 transient 修饰符的变量。我正在使用同样来自 Gson-extras 的 PostConstructAdapterFactory 向我的 classes 添加一个 PostConstruct 方法,以便在反序列化完成后和 [=] 中的任何函数之前可以重新分配瞬态变量的值41=]es 被调用。这也很好用。

我面临的挑战是我有另一组 class es,它们是多态的并且还具有瞬态变量,我希望在反序列化后执行 PostConstruct 方法。我所有同时使用 RuntimeTypeAdapterFactory 和 PostConstructAdapterFactory 的尝试都失败了。我可以同时注册两者,但是当我执行 PostConstructAdapterFactory 行为时,它的行为似乎覆盖了 RuntimeTypeAdapterFactory 行为,导致我的 classes 不再存储多态 classes 所需的 "type" 字段。

看来我可以选择其中之一,但不能同时选择两者。我什至考虑编写一个具有两个 Adapter Factories 功能的混合 AdapterFactory class,但这也没有成功。

我觉得我可能遗漏了一些关于这些设计方式的明显信息。我的 classes 可以同时使用这两种功能吗?下面是一个简单的例子来证明我在说什么。

public class BaseClass {
    private static final RuntimeTypeAdapterFactory<BaseClass> ADAPTER = RuntimeTypeAdapterFactory.of(BaseClass.class);
    private static final HashSet<Class<?>> REGISTERED_CLASSES= new HashSet<Class<?>>();
    static { GsonUtils.registerType(ADAPTER); }

    private synchronized void registerClass() {
        if (!REGISTERED_CLASSES.contains(this.getClass())) {
            REGISTERED_CLASSES.add(this.getClass());
            ADAPTER.registerSubtype(this.getClass());
        }
    }

    public BaseClass() {
        registerClass();
    }
}

public class DerivedClass extends BaseClass {
   public DerivedClass(Integer number) {
       super();
       this.number = number;
       Init();
   }

   protected Integer number;
   protected transient Integer doubled;
   protected transient Integer tripled;
   protected transient Integer halved;
   protected transient Integer squared;
   protected transient Integer cubed;

   public void Init() {
       halved = number / 2;
       doubled = number * 2;
       tripled = number * 3;
       squared = number * number;
       cubed = number * number * number;
   }

   @PostConstruct
   private void postConstruct() {
       Init();
   }

}

public class GsonUtils {
    private static final GsonBuilder gsonBuilder = new GsonBuilder().registerTypeAdapterFactory(new PostConstructAdapterFactory());

    public static void registerType(RuntimeTypeAdapterFactory<?> adapter) {
        gsonBuilder.registerTypeAdapterFactory(adapter);
    }

    public static Gson getGson() {
        return gsonBuilder.create();
    }    
}

Gson gson = GsonUtils.getGson(); 
String serialized = gson.toJson(new DerivedClass(6), BaseClass.class);
DerivedClass dc = gson.fromJson(serialized, DerivedClass.class);

更新:感谢@michał-ziober 的回答。按你说的做确实有效。它确实回答了这个问题,但它仍然没有完全解决我的问题。让我稍微修改一下问题。

我的代码的简化版本可能有点太简单了。当我尝试将 serialize/deserialize DerivedClass 作为另一个 class 上的 属性 时,我原来的问题被突出显示。如下:

public class WrapperClass {
    protected BaseClass dc = new DerivedClass(6);
}

Gson gson = GsonUtils.getGson(); 
String serialized = gson.toJson(new WrapperClass());
WrapperClass wc = gson.fromJson(serialized, WrapperClass.class);

在这种情况下,只要我的 DerivedClass 没有定义 PostConstruct 方法,DerivedClass 的 serialization/deserialization 就可以正常工作。但如果是这样,"type" 属性 不会写入文件。

重申一下,如果我直接 serialize/deserialize DerivedClass,一切都很好,但是如果它在 WrapperClass 中,我 serialize/deserialize WrapperClass 并且如果我定义了 PostConstruct 方法,它就不起作用。

使用类型适配器一切都很好,您可以同时使用它们。在您的情况下,问题在于执行代码的顺序。

  1. GsonUtils.getGson() - 使用适配器创建 Gson 对象。
  2. gson.toJson(new DerivedClass(6), BaseClass.class) - 您创建 DerivedClass 实例,该实例从 BaseClass 执行超级构造函数,该实例在适配器中注册给定 class(原文如此!)。

试试这个代码:

DerivedClass src = new DerivedClass(6);
Gson gson1 = GsonUtils.getGson();
String serialized = gson1.toJson(src, BaseClass.class);
System.out.println(serialized);
DerivedClass dc = gson1.fromJson(serialized, DerivedClass.class);

它将按预期工作。创建 DerivedClass 有一个副作用——这是一个很好的例子,为什么 class 应该相互分离。 BaseClass.

中不应有任何 Gson 特定的 class

最好的世界BaseClass应该只包含公共属性和一些基本逻辑:

class BaseClass {
    // getters, setters, business logic.
}

所有Gson配置应该放在GsonUtils class:

class GsonUtils {

    public static Gson getGson() {
        return new GsonBuilder()
                .registerTypeAdapterFactory(RuntimeTypeAdapterFactory.of(BaseClass.class)
                        .registerSubtype(DerivedClass.class))
                .registerTypeAdapterFactory(new PostConstructAdapterFactory())
                .create();
    }
}

如果您不想手动指定所有classes,您需要在运行时扫描环境。看看:At runtime, find all classes in a Java application that extend a base class.

问题更新后编辑

看起来 TypeAdapterFactory 链接并不像我预期的那么简单。这取决于给定的 TypeAdapterFactory 实现,它是 returns null 还是 returns 新对象与一些委托给其他工厂。

我找到了一个解决方法,但在实际项目中可能不容易使用:

  1. 我创建了两个 Gson 对象:一个用于序列化,一个用于反序列化。仅针对反序列化过程,我们注册 PostConstructAdapterFactory.
  2. 我们在 BaseClass.
  3. 中添加了带有 @PostConstruct 注释的方法

示例:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.typeadapters.PostConstructAdapterFactory;
import com.google.gson.typeadapters.RuntimeTypeAdapterFactory;

import javax.annotation.PostConstruct;

public class GsonApp {

    public static void main(String[] args) {
        RuntimeTypeAdapterFactory<BaseClass> typeFactory = RuntimeTypeAdapterFactory.of(BaseClass.class)
                .registerSubtype(DerivedClass.class);

        Gson serializer = new GsonBuilder()
                .registerTypeAdapterFactory(typeFactory)
                .create();

        Gson deserializer = new GsonBuilder()
                .registerTypeAdapterFactory(typeFactory)
                .registerTypeAdapterFactory(new PostConstructAdapterFactory())
                .create();

        WrapperClass wrapper = new WrapperClass();
        wrapper.setDc(new DerivedClass(8));

        String json = serializer.toJson(wrapper);
        System.out.println(json);
        System.out.println(deserializer.fromJson(json, WrapperClass.class));
    }

}

class WrapperClass {
    protected BaseClass dc;

    public BaseClass getDc() {
        return dc;
    }

    public void setDc(BaseClass dc) {
        this.dc = dc;
    }

    @Override
    public String toString() {
        return "WrapperClass{" +
                "dc=" + dc +
                '}';
    }
}

class BaseClass {

    @PostConstruct
    protected void postConstruct() {
    }
}

class DerivedClass extends BaseClass {
    public DerivedClass(Integer number) {
        super();
        this.number = number;
        Init();
    }

    protected Integer number;
    protected transient Integer doubled;
    protected transient Integer tripled;
    protected transient Integer halved;
    protected transient Integer squared;
    protected transient Integer cubed;

    public void Init() {
        halved = number / 2;
        doubled = number * 2;
        tripled = number * 3;
        squared = number * number;
        cubed = number * number * number;
    }

    @PostConstruct
    protected void postConstruct() {
        Init();
    }

    @Override
    public String toString() {
        return "DerivedClass{" +
                "number=" + number +
                ", doubled=" + doubled +
                ", tripled=" + tripled +
                ", halved=" + halved +
                ", squared=" + squared +
                ", cubed=" + cubed +
                "} ";
    }
}

以上代码打印:

{"dc":{"type":"DerivedClass","number":8}}
WrapperClass{dc=DerivedClass{number=8, doubled=16, tripled=24, halved=4, squared=64, cubed=512} }