【问题标题】:Deserialize an object after the underlying class structure got changed底层类结构改变后反序列化对象
【发布时间】:2013-04-06 00:15:54
【问题描述】:

我们正在序列化一些 Data 对象并将它们存储在数据库中,长期以来,数据库中有数百个这样的对象。最近我们需要改变其中一个类的结构。现在反序列化这些对象正在引发错误。

我们之前没有写自定义序列化和反序列化。 defaultSerialVersionUID 的使用在这里也对我们没有帮助,因为在序列化它们时它只是默认的 1L

在底层类结构发生变化后,有没有更好的方法来反序列化它们?

【问题讨论】:

    标签: java serialization


    【解决方案1】:

    我们正在序列化一些 Data 对象并将它们存储在数据库中......

    那是/现在是一个大错误,IMO。您避免了构建和维护与数据对象相对应的表……但是 Java 对象序列化并不是为这种事情而设计的,这种脆弱性是缺点之一。


    @EJP 和@sidoh 的答案提供了一些想法,可以让您摆脱当前的困境,前提是您可以掌握旧版本的课程。有几种方法:

    • 回滚到破坏兼容性的更改之前的代码/架构状态...并以避免错误的方式重做工作。

    • 不要回滚,而是编写一些 ad-hoc 转换器,将当前可读的对象加载为可读形式并更新存储的版本。

    前者可能不切实际;例如如果其他内容发生了太多变化,或者您的代码/数据正在生产中。

    后者涉及从版本控制中检索类的旧版本和新版本,并构建一个临时转换器,该转换器与旧类一起加载,转换为新类的实例并保存。这样做的困难在于构建一个可以同时在同一个 JVM 中使用相同类的两个不同版本的 Java 应用程序:

    • 您可以在其类路径上实例化两个具有不同版本的类加载器。但问题是 JVM 会将这两组类视为不同的类型,这将使针对这两个版本的静态绑定变得不可能。你可以通过反射来解决这个问题……但它会非常混乱,特别是如果相应的对象 API 很广泛的话。

    • 您可以将此作为一个两阶段的过程。第 1 阶段是使用旧类加载,然后使用(比如说)您写入文件(比如说)的 JSON 序列化这些类。第 2 阶段是读取 JSON,使用它使用新类创建对象,并使用对象序列化对其进行序列化。


    第三种选择是编写一个临时转换器,调整序列化对象。基本上,您需要了解新旧序列化表单之间的差异,然后通过使用一些低级 API 读取/写入它们来重写序列化对象。对此的一种变体是在新版本的类中实现自定义的 readObject 和方法,该类既能理解旧格式,也能理解新格式。但是,鉴于您的旧对象没有任何自定义版本字段,这可能会很棘手。需要注意的是,这种东西超出了序列化规范的范围……


    但 IMO 最好的选择是以此为契机停止使用 Java 序列化。使用旧类读取对象,然后将它们写回数据库到常规 SQL 表中,作为 JSON 或 XML blob,或者使用 ORM 映射。

    【讨论】:

    • 谢谢斯蒂芬,我们有点被这种情况锁定了。我们将这些对象与 Quartz 调度程序一起用作 JobData 以及其他一些重要信息。我们正在尝试先将更改还原回来,然后可能会编写一些实用程序来将所有内容更改为更复杂的方式。
    【解决方案2】:

    接下来,我强​​烈推荐Thrift 之类的东西。它的开发部分是为了解决这个问题。

    至于解决手头的问题,您可以还原更改吗?反序列化对象,将旧数据中的字段放入新对象,并序列化新对象。

    【讨论】:

      【解决方案3】:

      在进行这些更改之前,您应该阅读的第一件事是对象序列化规范的对象版本控制章节。它准确地列出了在保持与现有序列化的兼容性的同时可以做什么和不能做什么。它允许的内容比大多数人想象的要多得多,但它不允许例如继承链中的更改,这听起来就像您所做的那样。

      您必须做的第二件事是取消那些不符合要求的更改。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-05-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-02
        • 1970-01-01
        相关资源
        最近更新 更多