I use the Externalizable interface to solve this problem. It doesn't really meet your Appendable requirement, but it might get you started.
Externalizable lets you write each object out yourself. What I do is include a version number for each class. Then, when I change the class, I adjust the readExternal
method so it can can read the new format as well as the old ones and change the writeExternal
method to write the new format with the new version number. The code to read old formats may need to change to fill new fields (and not fill in removed fields). Then I'm ready to go.
There's usually some sort of object hierarchy in the graph, so if a level 2 class is seriously modified, the new write/read for that class can output/input something completely different from the old one, with completely new lower-level classes.
Some care is needed because sometimes there isn't a hierarchy. You have to remember that, after reading, you can't set a field by pulling data from objects in fields because they may not really be there yet. (Sometimes I add a setUp
method to all my classes that gets called after the top-level objectRead
so the objects in the graph can get themselves organized after the whole graph has been read.
And it sometimes helps to write out objects manually using a method that just writes out plain bytes. (But on the read you have to know precisely what you are reading, where readExternal
will read a complete object of any class.)
Code looks something like:
// version 3 -- November 16, 2013
// version 2 -- March 22, 2013
// version 1 -- April 1, 2012
public void writeExternal( ObjectOutput out ) throws IOException {
out.writeShort( 3 )
out.writeLong( longData );
out.writeObject( something );
....
}
public void readExternal( ObjectInput in ) throws IOException,
ClassNotFoundException {
short version = in.readShort();
if (version > 3) {
// Admit program is too old to read file.
}
else if (version == 3) {
longData = in.readLong();
something = readobject();
}
else if (version == 2) {
longData = 5;
something = readobject();
}
else
...
}
Xml deserialization - at least in case of C# (you're not referring to it directly, but your links indicate it as your language of choice) - has type recognition built in. Why not make use of that?
See https://stackoverflow.com/questions/775647/how-do-i-deserialize-xml-without-knowing-the-type-beforehand for more details.
1: Make some super class that contains every possibility of contained
types.
As demonstrated in the question I linked to above, all it takes is creating some common base class for your errorMessage
objects. Then you need to use XmlInclude
.
So the super class would only have to "contain every possibility" in the sense of knowing about all its subtypes (but not their implementation details). It's still all nice and neat in terms of OOP.
Note that if you feel that .NET serialization is too limiting, there's always more sophisticated alternatives such as SharpSerializer - better suited for polymorphic scenarios.
Again, this is not a language-agnostic suggestion, but if you used another programming language, the advice still applies: you can look for a good open-source library.
2: Make some mechanism for reading the xml first to identify what's in it.
You could also use reflection and retrieve the type in run-time (as Kirtan suggested) by the name of the root element, or some attribute of the root element.
Another option would be to create, or generate, XSD schemas for your errorMessage
items, then use these schemas to validate the incoming messages - thus establishing their "type identity".
One advantage of this admittedly a bit more troublesome solution is that if the incoming xml was somehow broken, xsd validation could tell you exactly where and why (eg. some unexpected, unrecognized element), you don't have to handle this "manually".
Then there's dynamic typing and yet another interesting approach:
https://stackoverflow.com/a/13705918/168719
A more elegant approach would be to implement some kind of Action
pattern, where, once the object was identified you could create the
proper action to take with it.
That's what takes place after you deserialize the message, doesn't it?
With regard to this, I don't think one could come up with suggestions superior to what you have already found in this thread: https://stackoverflow.com/questions/298976/is-there-a-better-alternative-than-this-to-switch-on-type
I hope this gives you some inspiration. If not, the principle I go by is: when it's awfully hard to solve a problem, most likely the problem itself is badly posed from the very beginning. So if you are stuck, maybe it's time to step back and redesign. I don't know broader context, so I have no way of knowing how possible it is in your case.
Best Answer
From
C++ perspective
there is a very informative article, which every developer needs to read at once. It basically lays down the difference in serialization for approached intrusive and non-intrusive.A practical guide to C++ serialization is another well written source to be aware of. More info from Boost C++ libraries on Serializable Concept.