Thursday, January 24, 2008

Serialize Me

I've been updating the animation system editor lately. More precisely on the saving/loading of nodes and connections.

The first thing that came to mind for the job was to use C# default xml serialization to do the work for me.

The animation engine has a diagram that contains all of it's nodes stored in a list of generic nodes. All nodes inherit from a base node class. I thought that the serializer would choose to write the subclass by default, but it has proven a little bit more complicated than that.

Disclaimer: Maybe I'm missing something out and doing it wrong. If anyone knows a better way to do this I'm really interested in knowing, so please contact me.

As it seems, a class that has subclasses that you may want to serialize needs to use the XmlInclude attribute and define what other classes it may represent...

e.g.

public class SubClass : SuperClass
{
//blah blah blah
}

[XmlInclude( typeof( SubClass ) )]
public class SuperClass
{
//etc etc etc
}


Any OO programmer should be running in circles, screaming and trying to pull out his eyes by now. Since when a superclass should have knowledge of all of its subclasses!?!

That's why I think I may be approaching this the wrong way. C# is too nice a language to have a shortcoming such as this one.

I'd also like to point out that my base node class resides in a separate assembly than my node subclasses, so even using that horrible solution is tricky.

In the end, I was wasting more time trying to get around this than doing real work, so I've used XmlReader and XmlWriter to save and load the node diagram manually.

How have I've solved the node type instantiation then?

When saving nodes I write a string with its type name.


BaseNode node = ( BaseNode ) diagram.Nodes[ nodeId ];

String type = node.GetType().ToString();

...

writer.WriteStartAttribute( "type" );
writer.WriteString( type );
writer.WriteEndAttribute();


And when reading them I use that string to instantiate the correct subclass dynamically. The only assumption I'm making here is that it has a default constructor.


reader.MoveToAttribute( "type" );
String typeString = reader.Value;

Type type = Type.GetType( typeString );
BaseNode node = ( BaseNode ) Activator.CreateInstance( type );


As nodes can have custom data, I've also created virtual functions in the base node class that subclasses can override to deal with that data. It may not be the most elegant solution, but it's quite flexible (and it works).

I think that the editor will soon be in a distributable (although feature limited, and nowhere near final) state and I'll be able to get some real feedback.

Next post I'll put some pretty drawings for my nonprogrammer friends :)

1 comment:

Cabron Tormenta said...

Sorry but I have to put this in spanish: "Una de cal y otra de arena xD"

About your problem... no idea.