When you use ObjectOutputStream to save, for example a save game to file, then you change the code - you break it.
I recall Cas said that he does this and breaks save games often with it.
Isn’t there something you can do to avoid it besides serializing it differently/yourself ?
Like, I would like it if a old/incompatible save game was loaded, it would only load the variables that still match and dont care about the rest.
Basically it should do “whatever it can”, load variables that still have the same name and type, forget about the stuff that doesnt match.
Not possible with ObjectOutputStream/Serializable ?
public class ClassName {
private static long serialVersionUID = 1L;
//REST OF YOUR CODE
}
A serialVersionUID is used to allow you to programmatically enforce a form of ‘version’ control on your serialized objects. When you use it, you say what the ID is, and control when that Id gets changed. If a object that has been serialized with a different Id is unserialized, you’ll get an exception. Basically, if your not changing the actual data representation of your code, such as the variables in your code, you can leave your serialVersionUID the same, and it probably will be able to unserialize correctly, regardless of what you did to the other code.
Not really what he wants - that’s the uncommon case where nothing data changes -
edit: -> this is where i went off the rails (it’s not actually what he wants either)
he wants (quite naturally) to avoid breaking the serialization of critical data when something in another part of the graph breaks. Java serialization doesn’t really go out of it’s way to help with this; but i can tell you that isolation of the unchanging data in a another file/stream is key:
Something like that should avoid most problems (except uniqueness == issues, which it may cause because of multiple graphs).
In retrospect serialization should have been position independent somehow if that was possible - as a generic protocol it’s utter failure when something as dynamic as code is represented.
If you try to do it as ‘it is meant to be used’ your major version readObject() conversions (if you don’t give up on that) will be byzantine - modify the order of the fields in a holder class? Hello serializable error, goodbye user state.
Now if you’re actually talking about changing the (supposedly final) user data; you need to overload readObject and check a version number or something like that you wrote to the stream.
You can maybe try a cascaded switch of versions, with default values on top. I don’t know because i am hoping to avoid that layer of hell forever, also, f**k the users.
I just thought of another way than reading stuff in ‘manually’ in readObject. You still need to do the transformations (that is, taking the old object and reading what you can to the new format), but it should be easier not having to deal with readObject:
[quote]The readResolve method is called when ObjectInputStream has read an object from the stream and is preparing to return it to the caller. ObjectInputStream checks whether the class of the object defines the readResolve method. If the method is defined, the readResolve method is called to allow the object in the stream to designate the object to be returned. The object returned should be of a type that is compatible with all uses. If it is not compatible, a ClassCastException will be thrown when the type mismatch is discovered.
[/quote]
So… keep old major version of the dataclass around, use interface, read replace old versions to new versions… profit? Not so sure if changing the interface would be allowed… prob not since the old versions implement it… but maybe if you delete that ‘implements’ and keep the serialversionUID. I dunno…
Or you can just bite the bullet and use versions since there is no magic available for the actual transformations.
Serialization is all right for transferring objects or caching them when you don’t mind blowing them away. It’s crap for any kind of long-term persistence. Do not use serialization for save games if you ever plan on changing any of the classes in development or a patch.
If you put a serialVersionUID in a class, Java already does what you want and attempts to read in as many variables as it can and ignores the rest. Generally though this means your save game is broken anyway. The problem cannot actually be magically solved by any other solution; Java already implements it exactly as you would do it yourself. I break saved games all the time because the changes I usually make are massively incompatible, so it’s usually totally deliberate.
sproingie reckons serialisation is crap for long-term persistence and he’s right in that it’s quite a pain to actually migrate data from an old version to a new version - like you could do with a database for example. It is however no more nor less tricky or crappy than using, say, XML, or your own proprietary solution, so don’t sweat it. Just make sure everything’s got a serialVersionUID. Eclipse has a warning that you can turn on for that.
Suffers exactly the same problems as serialization does, really, with the added bonus of massively verbose files. “Advantage” is you can hack the files by hand or with some other XML processing tool.
Actually this is what I wanted to hear.
In my case this problem is not for patches after the game has been released - but I use save games in debugging. While programming I change a lot obviously, it automatically saves where I was and stuff; and I just want to jump right in again.
So it would for example load the X and Y and current map which but not any new stuff.
Gonna try it out, but then it should work like I want it to.
Now, why doesn’t that surprise me? : Like I said, never used it or plan to - just seemed it might be the simplest way of getting XML saving without 3rd-party code.
I’m partial to multi-format storage backends using the same API. Jackson does XML, JSON, and BSON. You could write out saves as xml or json for debugging, and BSON when you want something compact.
Just to walk back a bit, Java serialization actually isn’t that bad if you have dedicated data objects and don’t just slap Serializable on your in-game classes. Just make sure to write lots of tests for your migration code if you do change the format.
I look at the amount of hair pulling, angst and grief I will get by breaking customers saved games and the resulting email support fallout versus the amount of time I’d spend making upgrade systems and versioning etc. Depending on the number of customers you can make a reasonably informed choice.
I going to use ObjectOutputStream for save games UNTIL release, for debugging, because its easy… and once I release, I write my own files that always work and stuff.
https://code.google.com/p/kryo/#Compatibility
Can support no compatibility (the default, FieldSerializer), adding fields only (TaggedFieldSerializer), and adding or removing fields (CompatibleFieldSerializer). There currently isn’t a serializer for changing field types, but it could be done.
Protobuf supports adding/removing fields and limited changing types, but you’ll have to maintain a schema by hand.