简体   繁体   English

如何将对象反序列化为具有相同名称的新对象?

[英]How to deserialize object to new object with the same name?

I have an application that previously stored some data by serializing the model object ( QuickNote ). 我有一个应用程序,该应用程序以前通过序列化模型对象( QuickNote )来存储一些数据。 This was then loaded by deserialization. 然后通过反序列化将其加载。

However, I am updating the application to store the data by XML instead and I need to be able to import the old, serialized object for conversion. 但是,我正在更新应用程序以改为通过XML存储数据,并且我需要能够导入旧的序列化对象进行转换。 The new object, which does not implement Serializable , also has the same name though.( QuickNote ). 尽管没有实现Serializable ,但是新对象也具有相同的名称。( QuickNote )。

I am trying to deserialize the old QuickNote and store it into the new one using the follow code: 我试图使用以下代码反序列化旧的QuickNote并将其存储到新的QuickNote

FileInputStream fileIn = new FileInputStream("data/quicknotes_user.dat");
ObjectInputStream in = new ObjectInputStream(fileIn);
List<OldQuickNote> newList = (List<OldQuickNote>) in.readObject();

Since the class name for the old serialized file is QuickNote , I get the error java.io.InvalidClassException: model.QuickNote; class invalid for deserialization 由于旧序列化文件的类名是QuickNote ,因此出现错误java.io.InvalidClassException: model.QuickNote; class invalid for deserialization java.io.InvalidClassException: model.QuickNote; class invalid for deserialization due to the new class having the same name and not implementing Serializable . 由于新类具有相同的名称并且未实现Serializable ,因此java.io.InvalidClassException: model.QuickNote; class invalid for deserialization

Is there a way to deserialize this QuickNote object into an OldQuickNote object? 有没有办法将此QuickNote对象反序列化为OldQuickNote对象? Both QuickNote and OldQuickNote are identical except that QuickNote no longer implements Serializable but OldQuickNote does. QuickNoteOldQuickNote都是相同的,只是QuickNote不再实现SerializableOldQuickNote却实现了。

I did try to search for answers, but to be honest, I am not even sure how to search for this as I obviously made a poor design decision when electing to serialize the QuickNote object in the first place. 我确实尝试搜索答案,但是老实说,我什至不知道如何搜索它,因为当我选择首先序列化QuickNote对象时,我显然做出了错误的设计决定。

Caveats: 注意事项:

  1. I cannot change the object type (class name) for the serialized object as it is now residing on the user's systems. 我无法更改序列化对象的对象类型(类名),因为它现在位于用户系统上。
  2. The new QuickNote object cannot be changed to implement Serializable because it is annotated and used for JAXB marshalling. 不能将新的QuickNote对象更改为实现Serializable因为它已被注释并用于JAXB编组。

One option is mentioned in the comments: force a migration of data to XML and then get rid of the old class. 注释中提到了一个选项:强制将数据迁移到XML,然后摆脱旧的类。

Another option is to try to parse it without the usage of Java Serialization machinery. 另一个选择是尝试在不使用Java序列化机制的情况下解析它。 There are some tools that parse java serialized data like https://github.com/gagern/nodeJavaDeserialization I could not find any library in java though, but, if you only have one class data to parse, the task to parse it does not seem so scary. 有一些解析Java序列化数据的工具,例如https://github.com/gagern/nodeJavaDeserialization我虽然在Java中找不到任何库,但是,如果您只有一个类数据要解析,则解析它的任务不会看起来好恐怖 The documentation on the format is available: http://docs.oracle.com/javase/6/docs/platform/serialization/spec/serialTOC.html , and ObjectOutputStream / ObjectInputStream source too. 也提供有关格式的文档: http : //docs.oracle.com/javase/6/docs/platform/serialization/spec/serialTOC.html ,以及ObjectOutputStream / ObjectInputStream源。

And one more option would be to play with the serialized data before deserializing. 还有一种选择是在反序列化之前处理序列化的数据。 Imagine that you rename your existing QuickNote to QuickNot2 (ugly, but will be useful later) and create your new class as QuickNote . 想象一下,将现有的QuickNote重命名为QuickNot2 (很难,但稍后会有用),然后将新类创建为QuickNote Then, before deserializing the QuickNot2 , you change that e to 2 in the serialized bytes representation. 然后,在反序列化QuickNot2之前,在序列化的字节表示形式中将e更改为2 Here is a proof of concept: 这是一个概念证明:

public class Data1 implements Serializable {
    private final static long serialVersionUID = 123L;

    private final String name;

    public Data1(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

public class Data2 implements Serializable {
    private final static long serialVersionUID = 123L;

    private final String name;

    public Data2(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

And

Data1 data1 = new Data1("hello");

ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream dos = new ObjectOutputStream(baos);
dos.writeObject(data1);
dos.close();
byte[] serialized = baos.toByteArray();

serialized[12] = '2';

ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(serialized));
Data2 data2 = (Data2) ois.readObject();
System.out.println(data2.getName());

This prints 'hello'. 这会打印“ hello”。

As you can see, here I serialize via one class and deserialize via another one by changing the name of the class in the serialized form (byte with index 12 is changed, it's where '1' is stored). 如您所见,在这里,我通过一个类进行序列化,并通过以序列化形式更改类的名称(通过更改索引12的字节,将其存储在“ 1”中)来对另一个类进行反序列化。

For this to work, the following must hold: 为此,必须满足以下条件:

  1. Both classes (the old QuickNote and that QuickNot2 must have the same structure 这两个类(旧的QuickNoteQuickNot2必须具有相同的结构
  2. They both must have the same serialVersionUID , so if it is not written in your code explicitly, you'll need to find it out and specify it in your code. 它们都必须具有相同的serialVersionUID ,因此,如果未明确将其写入代码中,则需要找出并在代码中进行指定。 To find it out, you can just set some arbitrary value in your QuickNot2 class and see the value in the error message which is produced by deserializer. 为了找出QuickNot2 ,您可以在QuickNot2类中设置一些任意值,然后在反序列化器生成的错误消息中查看该值。

QuickNot2 is suggested to leave class name length unchanged. 建议使用QuickNot2保持类名长度不变。 It seems possible to change the class name fully, with its length, I just did not check this. 似乎可以完全更改类名及其长度,只是我没有检查。

This was answered in the comments but the one with the answer didn't post it as such. 有人在评论中回答了这个问题,但是没有回答的人没有这样发表。

I ended up moving the old class to a different package and converted the class when the user ran the program again. 我最终将旧的类移到了另一个包中,并在用户再次运行程序时转换了该类。

All I had to do then was wait for my users to all update the the new version and deleted the old class. 然后,我所要做的就是等待用户全部更新新版本并删除旧类。

Thank you! 谢谢!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM