There are two types of personalities, among software engineers -- two types of personalities in general, really -- the aggressively controlling, impatient "Type A" personalities, and the more passive, easy-going "Type B" personalities.
As one of the first people to step up with a custom code-generator for strongly-typed collection classes, I lean strongly toward the former. But even I know when I'm beat. For example, when faced with a reflection-based serialization architecture which requires
- public, parameterless constructors on all my classes
- public accessibility on all my fields and properties
- never creating a circular loop of object references
- never caring about the instance-identity of my objects
- never caring about the base-class type identity of my objects
I know that I have no prayer, no hope of applying this technology directly to the code I need to write, in order to do my job. No cute tricks, magic attributes, or fancy design patterns are going to save the situation.
I'm talking, of course, about XML Serialization: the System.Xml.Serialization.XmlSerializer class. It does not play nice with the other classes in your project! Consider yourself warned.
Now, we can either whine about this, or we can treat this crisis as an opportunity -- if we accept, from day one, that we can not apply XML Serialization attributes directly to the meat of our code, and that we'll have to generate and maintain a whole set of classes tailored for just this purpose... why not factor those classes out into a separate assembly? This gives us a nice, lightweight, effortlessly redistributable wrapper for reading and writing our file format.
Further, if we place those XML-serializable classes into their own namespace (I like to tack on ".Serialization") we can easily reference those classes in richer, fully-functional business logic classes of the same name -- woohoo, crisitunity!
Here's an example of how one might leverage XML Serialization in a WinForms application:
- Foo.exe
- A hypothetical WinForms app for editing .foo documents -- contains types mostly in the Jitsu.Foo namespace, including Jitsu.Foo.Document, a high-level representation of the .foo document model, including normalized references, lots of validation logic, a dirty bit, modification events, and so forth.
- Jitsu.Foo.Serialization.dll
- .NET assembly containing the XML Serialization code for .foo documents -- types in the Jitsu.Foo.Serialization namespace, containing almost no logic at all. The richer classes (like Jitsu.Foo.Document, above) handle saving/loading themselves into/outof these raw serialization types.
I tend to think of defining an XML layout with C# classes in much the same way we used to define binary file formats with structs, in C. For example, have a look at BITMAPFILEHEADER in WinGdi.h or IMAGE_NT_HEADERS in WinNT.h -- used to describe the formats of the file-headers for bitmap files and PEFF executable files, respectively. The concept is identical -- the only difference is in the medium used to distribute the type information: header files for C, assemblies for .NET.
Like the file-formatting structs we see in the Win32 headers, I typically don't write one line of executable code into my XML-serializable types. I even incur the wrath of FxCop by using public fields instead of properties (this may be the one and only justifiable case for that taboo). So far, the only case I've encountered for mixing procedural code with XML Serialization is to perform custom formatting of date and time elements -- currently, XML Serialization only supports one format for System.DateTime: ISO-8601 local time. If you want something else, like UTC time, you'll have to hide the DateTime field behind a property getter/setter of type string.
