1

I have an array of objects that I am able to read and write to disk. But now I want to add a member variable to my class. If I add the member variable, I'll get a class incompatible error and I won't be able to read my serialized objects. Is there a way to modify the member variables of a class used for serialization without losing the ability to read persisted objects...or an easy way to convert the persisted objects to the new class?

486DX2-66
  • 159
  • 2
  • 10

5 Answers5

2

First of all, Java serialization isn't intended for object persistence, as the problem you are having highlights.

There is however a workaround (which will get you going in the short term) - add a serialVersionUID to your class and do not change when you modify the class in ways that effect its serialized form.

This is, imho, an abuse of the serialVersionUID but will fix your problem allowing you to add and remove fields at the expense that your system may become brittle if different JVMs use different versions of your class (there's ways to deal with this other than incrementing the serialVersionUID detailed in the link in @EJP's answer) as fields are set to default values meaning while your system may not throw exceptions when the results are not what the user expected and you have a subtle bug that's much harder to find than a serialization error.

All classes get a serialVersionUID - one is generated by the serialization runtime if you haven't declared one. By declaring a serialVersionUID you're telling the serialization runtime, don't generate one because you know the serialized form of the classes is compatible with one built earlier, for example if you added a toString method after the initial compilation that required a rebuild or have taken steps to ensure different versions serialize correctly (again, the link in @EJP's answer).

Nick Holt
  • 33,455
  • 4
  • 52
  • 58
  • If I had instead wrote my array of objects to disk as a byte array, then I would not have been able to modify the object at all. At least serialization allowed me the flexibility of adding fields and I presume also methods. I can't think of a more flexible and also simple solution for what I am trying to do, but if you have any suggestions, please let me know. I've used embedded databases such as SQL Lite for other projects and felt that was overkill for what I'm trying to do. – 486DX2-66 May 07 '14 at 19:22
  • @486DX2-66 the problem whether you use Java serialization or a database is the same, when a class changes, previously saved instances of that class must reflect those changes or the load mechanism must be able to adapt to those changes. With serialization modifying the data isn't an option (unless you can unpick the serialization format), while with a database you'd typically write update scripts to account for the new field. In your case I'd probably go for a human readable form like XML or JSON, which can be modified as needed to reflect changes in your class – Nick Holt May 08 '14 at 09:01
  • (1) It's not 'an abuse of the `serialVersionUID` system'. It is exactly what it is for. (2) The default `serialVersionUID` is generated not by the compiler but by the JVM. – user207421 May 08 '14 at 23:42
  • @EJP point noted (and answer edited) about the point of generation, however it's, imho, an abuse not to increment the `serialVersionUID` when the serialized form changes - isn't that what it's indicating, that an object stream is compatible with a given class? – Nick Holt May 09 '14 at 06:26
  • @NickHolt The change suggested here doesn't break compatibility. You can add and delete fields to your heart's content. Ergo the `serialVersionUID` should not be changed. – user207421 May 09 '14 at 06:36
  • @EJP sorry I haven't explained myself clearly, yes serialization would work, but in a distributed system, stability could be effected. This happens because as objects pass from JVM to JVM, field values are lost depending on the version of the class they are using – Nick Holt May 09 '14 at 06:57
  • Stability of what exactly? Presumably you wouldn't be making such changes unless you knew the application would cope correctly, but there is no instability introduced at the Java level. It's guaranteed by the specification. – user207421 May 09 '14 at 07:15
  • Maybe stability was the wrong word - what I meant was, in a distributed system, it's easy for classes to get out of sync between the JVMs. I've typically seen this occur as your typical 'works-on-my-machine' type problem - you search the code looking for the bug but you can't reproduce it. Finally you compare timestamps of the JARs being used and find they're different. So I suppose what I'm saying is I'd rather fail-fast during deserialization than having a subtle bug that shows up in testing because the numbers are wrong – Nick Holt May 09 '14 at 07:43
  • @NickHolt You may personally be prepared to completely ignore a major versioning facility because of a configuration management problem, but that doesn't mean you should be recommending everybody else to do the same without explanation, and it certainly does not excuse describing correct use of the system as an 'abuse'. – user207421 May 09 '14 at 08:47
  • @EJP I wasn't saying ignore the versioning facility, I was saying be aware that simply setting the `serialVersionUID` to a value and never changing it is a trade-off and trying to highlight what that trade off is – Nick Holt May 09 '14 at 09:50
2

This post from SO that should answer your question. It even explains how to find the serial version automatically created by compiler if you didn't specify it (find serialver in referenced page). Old articles can still be interesting ... In short for the impatient :

  • serialization automagically deals with new or removed members provided serialversion does not change
  • IDEs or the serialver tool of the JDK can give you the serial version automatically generated by the compiler if you did not specify it

So you just have to find this serialversion, add the magic field static final long serialVersionUID = xx and as you are only adding a member variable all should be fine.

(The referenced post is first answer from google for java serialization multiple versions)

Community
  • 1
  • 1
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
2

If I add the member variable, I'll get a class incompatible error.

No you won't, unless you forgot to define a serialVersionUID value for the class. If you did, use the serialver utility on the original version of the class to tell you what to add.

Adding a field to a serializable class is entirely benign. See the Versioning chapter of the Object Serialization Specification for full details of what you can do to a class before breaking its compatibility under serialization.

user207421
  • 305,947
  • 44
  • 307
  • 483
0

After adding the class variable make sure both the sending and the receiving sides have the new version of this class they are serializing/deserializing. That should fix your issue.

Of course, also regenerate the disk/serialized data.

peter.petrov
  • 38,363
  • 16
  • 94
  • 159
  • 2
    This doesn't really address the question - the OP appears to be using serialization as a persistence mechanism (I'm assuming that from the reading/writing to disk part). They're now unable to read their data because it was written with a previous version of the class, so while it's true that if you were sending an object between client and server, both sides should use the same class, that advice isn't applicable in this case – Nick Holt May 07 '14 at 16:12
  • Yes, Nick phrased the issue better than I could. That's the problem that I am trying to resolve. – 486DX2-66 May 07 '14 at 19:14
  • @NickHolt I used the terms 'sending/receiving sides' on purpose. I never mentioned client and server. Replace sending=persisting, receiving=loading and it makes sense. How come this does not address the OP's problem, it's exactly addressing the OP's problem IMHO... Anyway, never mind. Good that the OP solved the problem anyway. – peter.petrov May 09 '14 at 08:38
-1

Just to chip in, incrementing the serialVersionUID is a good practice. Serialization uses it to ensure the classes on both ends are the same and the saved data is compatible with the current classes.

====

After looking at some of the comments, I want to add why serialVersionUID can be a BAD thing. Bottom line, it must be maintained manually. In a shop with strong procedures in place, that's not a problem. However, simply adding one and then ignoring it or updating it half the time leads to a false sense of security. It appears to be helping, but it's not.

Here are a few thoughts:

  • The compiler issues a warning if a Serialiable class doesn't have a serialVersionUID, so add one. Allowing warnings to accumulate makes them useless.
  • Create a standard and then follow it. Choosing not to update the value is valid, but it means you have to find another way to keeping the class matched between systems.
  • If you don't intend to update the value as the class properties change, then set the value to -1 as an indicator.
Steve11235
  • 2,849
  • 1
  • 17
  • 18
  • I don't know whether just adding a `serialVersionUID` is good practice - serialization will work with incompatible classes because `serialVersionUID`s match, that's the only check that's performed. If you've added fields, as in the OP's case, the object will serialize/deserialize but if you've got two JVMs talking over RMI you'll be losing data – Nick Holt May 07 '14 at 16:04
  • @Nick Please note that I said "incrementing", not "just adding". I agree that serialVersionUID can be misleading if it is not maintained. – Steve11235 May 07 '14 at 19:27
  • It's a poor practice, and you will be unable to cite a Sun or Oracle document that says otherwise. It's just an unfortunately very widespread misapprehension. – user207421 May 08 '14 at 23:43
  • @EJP From the Java 7 Serializable API: "The serialization runtime associates with each serializable class a version number, called a serialVersionUID, which is used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization." I assume you modded me down; please remove your down vote. – Steve11235 May 09 '14 at 14:14
  • @Steve11235 I know what it says in the API, and the Specification too, and there is nothing in either place about changing the `serialVersionUID` when you change the class.. – user207421 Feb 23 '16 at 22:41