防止 Java 序列化设置默认值
我有一个包含整数 foo 的序列化对象 MyObject。我将值 10 设置为整数 foo 并使用 writeObject() 将对象保存到文件中。
我将整数条添加到对象 MyObject。我将值 15 设置为整数 bar,然后使用 readObject() 加载旧的序列化文件。
旧的可序列化文件不包含整数 bar,因此整数 bar 将获得值 0。如果旧的可序列化文件不包含变量 bar,我想在 bar 中保留值 15。
我应该覆盖 readObject() 或者如何阻止 readObject() 将“默认值”设置为未知对象?
我想这样做是因为在构造函数中我设置了自己的默认值,并且希望使用自己的默认值来控制版本控制。
I have a serialized object MyObject that contains integer foo. I set a value 10 to integer foo and save the object to a file using writeObject().
I add integer bar to object MyObject. I set a value 15 to integer bar and then load the old serialized file using readObject().
The old serializable file doesn't contain integer bar so integer bar will get value 0. I want to keep the value 15 in bar if the old serializable file doesn't contain variable bar.
Should I override readObject() or how could I prevent readObject() from setting "default values" to unknown objects?
I want to do this because in the constructor I'm setting my own default values and would like to use my own default values to control versioning.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
序列化不设置默认值,它遵循 Java 的默认值初始化方案。
如果我能总结一下你的问题。您希望序列化将序列化流中的内容与内存中的值合并。对于 Java 序列化来说这是不可能的,因为它控制要创建的对象。您可以读取序列化对象,然后手动编写代码来合并要合并在一起的字段。如果您坚持使用 Java 序列化(如果我是您,我会避开它),但假设您想继续使用它。
Viola 您可以控制哪些字段将从该字段合并到 someObject 中。如果您希望库为您执行此合并,请查看http://flexjson.sourceforge.net。它使用 JSON 序列化,并通过 Bean 而不是 POJO 工作。但是,有一种方法可以获取已填充的对象并覆盖 JSON 流中的值。这有其局限性。这样做的另一个好处是,在对象结构发生更改后,您实际上可以读回流,而 Java 序列化在技术上可以做到这一点,但它非常非常困难。
Serialization doesn't set default values it defers to Java's default value initialization scheme.
If I can sum up your question. You want serialization to merge what's in the serialized stream with the values in memory. That's not possible with Java serialization as it controls what objects to create. You can read in your serialized object, then manually write the code to merge what fields you want merged together. If your stuck on Java serialization (I'd steer clear of it if I were you), but let's say you want to continue using it.
Viola you control which fields will be merged from that into someObject. If you want a library to do this merge for you check out http://flexjson.sourceforge.net. It uses JSON serialization, and works from Beans rather than POJO. However, there is a way to take an already populated object and overwrite values from a JSON stream. There are limitations to this. The other benefit of this is you can actually read the stream back AFTER your object structure has changed something that Java serialization can technically do, but its very very hard.
将以下方法添加到您的 MyObject 对您有用吗?
Would adding the following method to your MyObject work for you?
这里的问题是
ObjectInputStream
不会调用默认构造函数MyObject()
,它通常也会初始化字段值。 (例如foo=10
、bar=15
)ObjectInputStream
生成自己的空构造函数用于反序列化,该构造函数不会初始化字段! (仅使用流中的值)但是此问题有一个解决方法,如下所示:
创建一个不可
可序列化
的超类MyParent
。 (MyObject 扩展 MyParent
)默认构造函数
MyParent()
将由ObjectInputStream
调用,因为MyParent
不是Serialized
。创建一个空方法,例如
MyParent.setDefaults()
,由MyParent()
构造函数调用。在
MyObject.setDefaults()
子类中重写该方法,以在ObjectInputStream
从流中读取值之前设置默认值。示例:
The problem here is that
ObjectInputStream
does not invoke the default constructorMyObject()
which usually also initializes the field values. (e.g.foo=10
,bar=15
)The
ObjectInputStream
generates its own empty constructor for deserialization, which does not initialize the fields! (Only uses the values in the stream)But there is a workaround for this problem as follows:
Create a superclass
MyParent
which is NOTSerializable
. (MyObject extends MyParent
)The default constructor
MyParent()
will then be invoked by theObjectInputStream
becauseMyParent
is NOTSerializable
.Create an empty method e.g.
MyParent.setDefaults()
which is called by theMyParent()
constructor.Override that method in the subclass
MyObject.setDefaults()
to set your default values BEFORE theObjectInputStream
reads the values from the stream.Example:
仅使用方法“merge(obj)”的问题是,您无法区分流中已保存的旧默认值“bar=0”(情况 X >)以及没有该字段的更旧的已保存对象(情况 Y),然后与新更改的默认值(从“0”到“15”)合并。
在这种情况下,方法“merge(obj)”可能会为之前已经存在的字段使用新的默认值“15”,但在(情况 X)中,它之前已被用户保存/更改为值“0”。
情况 X:它应该从流中保存的对象恢复“0”,并且不应该使用新的默认值” 15 英寸。
情况 Y:对于更旧的已保存对象(流中没有字段),它应该使用新的默认值“15”。
您可以(当然)只引入一个新的“版本”字段,然后可以通过合并方法处理该字段,但这需要更多代码。
情况 Z :如果我们引用其他对象,就会出现另一个问题。
然后调用者和合并方法还需要知道调用所有引用的合并方法(递归)。
基本上,这正是 ObjectOutputStream 和 ObjectInputStream 已经使用 readObject(ois) 等方法所做的事情。
请参阅我使用 setDefaults() 的解决方法,它将在 ObjectInputStream 恢复之前调用来自流的值,和/或使用带有 readObject(ois) 的解决方案,这也可以避免该问题。
The problem with only using a method "merge(obj)" is that you cannot distinguish between an already saved old default value of "bar=0" in the stream (Case X) and an even older saved object without that field (Case Y), and then merging with a newly changed default value from "0" to "15".
In that case the method "merge(obj)" would maybe use the new default value "15" for the field that already existed before, but in (Case X) it was already saved/changed earlier with value "0" by the user.
Case X: It should restore "0" from the saved object in the stream, and SHOULD NOT use the new default value "15".
Case Y: For the even older saved object (without the field in the stream), it SHOULD use the new default value "15".
You could (of course) just introduce a new "version"-field, which then can be handled by the merge-method, but that requires much more code.
Case Z: Another problem happens if we have references to other objects.
Then the invoker and merge-methods would need to be aware of invoking the merge-methods of all references as well (recursive).
Basically, that's exactly what ObjectOutputStream and ObjectInputStream already do using the methods readObject(ois), etc.
See my workaround with setDefaults(), which will be invoked before the ObjectInputStream restores the values from the stream, and/or use the solution with readObject(ois), which also avoids that problem.
使用关键字
transient
从序列化/反序列化中排除字段。Use the keyword
transient
to exclude fields from serialization/deserialization.