What can you do when serialization helper properties just aren't enough?
Bring out the big guns and write your own ContentTypeSerializer.
If you have ever written a ContentTypeWriter or ContentTypeReader, this should seem familiar. To customize the XML serialization for this test class:
class TestClass { public int elf = 23; }
We create this helper:
[ContentTypeSerializer] class TestClassSerializer : ContentTypeSerializer<TestClass> { protected override void Serialize(IntermediateWriter output, TestClass value, ContentSerializerAttribute format) { int hundreds = value.elf / 100; int tens = (value.elf / 10) % 10; int ones = value.elf % 10; output.Xml.WriteStartElement("elf"); output.Xml.WriteString(string.Format("{0} hundreds, {1} tens, and {2} ones", hundreds, tens, ones)); output.Xml.WriteEndElement(); } protected override TestClass Deserialize(IntermediateReader input, ContentSerializerAttribute format, TestClass existingInstance) { // TODO: this part left as an exercise for the reader :-) return new TestClass(); } }
Whenever IntermediateSerializer encounters a TestClass instance, it will now call TestClassSerializer.Serialize, producing this XML:
<XnaContent>
<Asset Type="TestClass">
<elf>0 hundreds, 2 tens, and 3 ones</elf>
</Asset>
</XnaContent>
Neat, huh? ContentTypeSerializer gives direct access to the XmlWriter and XmlReader, and thus total flexibility to do absolutely anything you can imagine.
Best of all, I didn't have to change my TestClass to do this. That means games can have a data class shared between their content build and game code, plus a custom ContentTypeSerializer which is only referenced by the Content Pipeline build, so the custom serialization code does not have to be shipped as part of the final game.
Note, however, that writing XML serialization entirely by hand is hard work. This is not something you want to do any more than you absolutely have to!
Note 2: there is a bug in Game Studio versions 2.0 and prior, which means the custom ContentTypeSerializer will be ignored if you call IntermediateSerializer directly from a program of your own. This will only work if IntermediateSerializer is called inside the Content Pipeline build process, from an importer or processor. This is fixed in the 3.0 framework.