当前位置: 首页 > 知识库问答 >
问题:

Java序列化-自动处理更改的字段?

周墨一
2023-03-14

线程“main”java.io.InvalidClassException中的异常:TestData;字段编号的类型不兼容

我现在还在下面提供了一个示例。

我正在使用一个大型应用程序,它将序列化对象(实现可序列化,而不是可执行)从客户机发送到服务器。不幸的是,我们现在有一种情况,即现有字段完全改变了类型,这破坏了序列化。

谢谢你的建议。

5月20日更新-以下示例源--TestData中的字段“number”从旧版本中的“int”变为新版本中的“long”。注新版本的TestData中没有调用readObject(),因为异常在到达之前就引发了:

线程“main”java.io.InvalidClassException中的异常:TestData;字段编号的不兼容类型

javac -g -classpath .;old WriteIt.java

javac -g -classpath .;new ReadIt.java
java -classpath .;old WriteIt  //Serialize old version of TestData to TestData_old.ser

java -classpath .;new ReadIt  //Attempt to read back the old object using reference to new version
import java.io.*;


public class TestData
    implements java.io.Serializable
{
    private static final long serialVersionUID = 2L;

    public int number;
}
import java.io.*;


public class TestData
    implements java.io.Serializable
{
    private static final long serialVersionUID = 2L;

    public Long number; //Changed from int to Long


    private void readObject(final ObjectInputStream ois)
        throws IOException, ClassNotFoundException
    {
        System.out.println("[TestData NEW] readObject() called...");
        /* This is where I would, in theory, figure out how to handle
         * both the old and new type. But a serialization exception is
         * thrown before this method is ever called.
         */
    }
}

writeit.java-将TestData的旧版本序列化为'testdata_old.ser'

import java.io.*;

public class WriteIt
{
    public static void main(String args[])
        throws Exception
    {
        TestData d = new TestData();

        d.number = 2013;

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("TestData_old.ser"));

        oos.writeObject(d);

        oos.close();

        System.out.println("Done!");
    }
}

readit.java-尝试将旧对象反序列化为新版本的TestData。未调用新版本中的ReadObject(),这是因为事先发生了异常。

import java.io.*;

public class ReadIt
{
    public static void main(String args[])
        throws Exception
    {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("TestData_old.ser"));

        TestData d = (TestData)ois.readObject();

        ois.close();

        System.out.println("Number = " + d.number);
    }
}

共有1个答案

海雪松
2023-03-14

您的直接问题似乎是您没有为您的类定义固定的serialVersionUID字符串。如果不这样做,对象序列化和反序列化代码将根据发送和接收的类型的表示结构生成UID。如果在一端而不是另一端更改类型,则UID不匹配。如果修复了UID,那么读取器代码将通过UID检查,并且ReadObject方法将有机会“处理”序列化数据中的差异。

除此之外,我的建议是:

>

  • 尝试同时更改所有客户端和服务器,这样就不需要使用readObject/writeObject黑客攻击

  •  类似资料:
    • 问题内容: 我有一个Java类,该类存储在HttpSession对象中,该对象在集群环境中的服务器之间进行了序列化和传输。为了便于说明,我们将此类称为“人员”。 在改进代码的过程中,该类从“ com.acme.Person”移动到“ com.acme.entity.Person”。在内部,该类保持完全相同(相同的字段,相同的方法,相同的所有内容)。 问题是我们有两组服务器同时运行旧代码和新代码。具

    • 问题内容: 我们有一个Hadoop集群,我们在上面存储使用Kryo(序列化框架)序列化为字节的数据。我们用于此目的的Kryo版本是从2.21正式版本派生而来的,以将我们自己的补丁应用于我们使用Kryo遇到的问题。当前的Kryo版本2.22也解决了这些问题,但是具有不同的解决方案。结果,我们不能仅仅更改我们使用的Kryo版本,因为这意味着我们将不再能够读取已经存储在Hadoop集群中的数据。为了解决

    • 我需要序列化一个包含字符串的文档,如

    • {“登录”:“testuser”,“邮件”:“testuser@gmail.com”,“密码”:“123abc”} 现在的问题是:我听说我们不应该将密码存储为字符串,而是在Java中存储为char数组,所以我就是这么做的。 下面是我的用户类 在决定接受json作为请求之前,我使用byte[]作为密码,这就是它在数据库中的样子,也是我希望它现在的样子。 bAz}+øeí'méjé7héubfpléa

    • Jackson反序列化器具有@JacksonInject注释,以便在反序列化之前更改值,覆盖原始值。在序列化过程中有没有办法做到这一点? @JsonView注释有助于删除特定字段。 在调用REST api之前,Jackson应该用一个特殊字符(如*etc)覆盖特定的敏感字段值(它曾经被注释过)

    • 如题目所述,想请教一下大佬们在Java中序列化与反序列化的意义是什么,如何理解Java的序列化和反序列化?