The XML serialization API in .NET is pretty cool, but if you really want your data to look hype and futuristic, you can try out XAML!
XAML is not just for WPF or WF, it’s actually a generic XML-based language used to describe hierarchies of .NET objects. You can therefore use it to serialize your own stuff. However, unlike the XmlSerializer which you can twist into writing and reading any XML, the XAML serializer will conform to the XAML “standard”.
Let’s start with some simple stuff. Because I saw Iron Man not too long ago, and Firefox 3, about to be released, has this “robots” theme going on, let’s describe a robot in XAML.
Here’s the Robot class, and related classes:
public class Robot
{
public string Name { get; set; }
public Engine Engine { get; set; }
public Weapon Weapon { get; set; }
}
public abstract class Engine
{
public abstract int Power { get; }
}
public abstract class Weapon
{
public abstract int Damage { get; }
public abstract int AmmunitionCount { get; }
}
Let’s quickly serialize it in XAML:
static void Main(string[] args)
{
Robot r = new Robot()
{
Name = "Tony Stark",
Engine = new ElectricEngine(),
Weapon = new MachineGunWeapon()
};
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
using (XmlWriter writer = XmlWriter.Create("robot.xaml", settings))
{
XamlWriter.Save(r, writer);
}
}
Note that you need to reference WindowsBase and PresentationFramework for your project to compile. The XAML serializer is an implementation part of WPF, so you need WPF even though you’re not really using it. I think that’s because the XAML serializer is specifically tweaked for WPF, the same way Silverlight has its own XAML serializer too. The XAML syntax, however, is independent from those frameworks. For now, we have to use WPF’s serializer, but maybe in the future we’ll see a more generically purposed serializer made available.
Here’s the resulting “robot.xaml” markup:
<?xml version="1.0" encoding="utf-8"?>
<Robot Name="Tony Stark" xmlns="clr-namespace:XamlFun;assembly=XamlFun">
<Robot.Engine>
<ElectricEngine />
</Robot.Engine>
<Robot.Weapon>
<MachineGunWeapon />
</Robot.Weapon>
</Robot>
If you’re running Visual Studio Express, remember that the output file will end up either in the Debug or Release directory depending on how you launched the program (F5 vs. Ctrl+F5), unless you told Visual Studio that you know what you’re doing.
The first thing to change is to make it look a bit more professional. What’s that ugly XML namespace? Let’s have a fancy one. This is done using the XmlnsDefinitionAttribute, set on the whole assembly:
[assembly: System.Windows.Markup.XmlnsDefinition("https://ludovic.chabant.com/xaml", "XamlFun")]
Now the markup looks slightly better:
<?xml version="1.0" encoding="utf-8"?>
<Robot Name="Tony Stark" xmlns="https://ludovic.chabant.com/xaml">
<Robot.Engine>
<ElectricEngine />
</Robot.Engine>
<Robot.Weapon>
<MachineGunWeapon />
</Robot.Weapon>
</Robot>
In case you have several XML namespaces in your markup (e.g. you’re serializing data from several different .NET namespaces, or different libraries and APIs), you might want to suggest a cool namespace name to use for your stuff. This is done using the XmlnsPrefixAttribute:
[assembly: System.Windows.Markup.XmlnsPrefix("https://ludovic.chabant.com/xaml", "fun")]
This will tell the XAML serializer to use an XML namespace called “fun” to serialize our stuff. This is just a suggestion, though, so it can still choose another namespace. In our case, since there’s no collision, the serializer decides that it can use the main namespace.
Now, you might complain that this markup is incredibly verbose for what it describes. I mean, XML is pretty verbose most of the time, but this is crazy. Let’s fix that.
You will notice that the “Name” property was serialized to an XML attribute, and it’s the sensible thing to do in this case. The “Engine” and “Weapon” properties, however, have been serialized to XML elements because they’re .NET objects, and the serializer doesn’t know if it can express them in a more compact way. Since XML doesn’t have much else besides elements and attributes, if you’re not happy with an element, it means you want an attribute, which means you want a string.
As a .NET programmer, you know about TypeConverters, so your first bet is to create one for the Engine type that will convert to a string. This one is very simple, and just converts the Engine instance to the name of the Engine’s implementation type.
public class EngineTypeConverter : TypeConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
return true;
return base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
{
Engine engine = (Engine)value;
return engine.GetType().Name;
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
Then you decorate the Engine type appropriately (if you want to use this converter only for the Robot’s Engine property, and not for all instances of Engine, you can put the TypeConverterAttribute on the property instead of the type):
[TypeConverter(typeof(EngineTypeConverter))]
public abstract class Engine
{
public abstract int Power { get; }
}
However, this doesn’t change much. Running the program gives you the same markup as before. This is because the XAML serializer is not completely stupid, and won’t serialize something it won’t be able to deserialize later. In our case, we don’t say how we can also convert a string to an Engine! Adding the following lines to the EngineTypeConverter fixes the problem, even though we don’t implement the ConvertFrom method:
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
return true;
return base.CanConvertFrom(context, sourceType);
}
Running the program with breakpoints in both CanConvertTo and CanConvertFrom tells us that, indeed, the XAML serializer is querying for conversion to and from string. It’s actually asking for this quite a few times, and I’m wondering if this could be optimized in a future service pack for .NET 3.0… You’ll also realize that it’s querying for conversion to and from another type, MarkupExtension, but we’ll get to this in part 2 of this series.
Now here’s our markup:
<Robot Name="Tony Stark" Engine="ElectricEngine" xmlns="https://ludovic.chabant.com/xaml">
<Robot.Weapon>
<MachineGunWeapon />
</Robot.Weapon>
</Robot>
Much better, but not quite superb either. The problem here is that we can’t customize much of the engine. Our XML attribute only specifies the type of engine we’re using, and if we were to implement the ConvertFrom method on the EngineTypeConverter, we would only be able to build a new instance of the given type, and that’s it. It wouldn’t even quite work well because we’re not printing the full name of the type.
The problem we have here is that we want to make it easy for users and designer applications to read and write our robot markup. If we have a few “built-in” engines, we want it to be “natural” to use them.
Let’s say we have the following built-in engines: an electric engine, a nuclear engine, and a coal engine (because I love steampunk stuff).
public class ElectricEngine : Engine
{
public override int Power
{
get { return 2000; }
}
}
public class NuclearEngine : Engine
{
public override int Power
{
get { return 10000; }
}
}
public class CoalEngine : Engine
{
public override int Power
{
get { return 8; }
}
}
It would be nice if we could write XAML like this:
<Robot Name="Tony Stark" Engine="Nuclear" />
But on the other hand, if we want to use a more complex engine like the following CustomizableEngine, we want it to revert back to the XML element based syntax:
public class CustomizableEngine : Engine
{
private int mPower;
public override int Power
{
get { return mPower; }
}
public int PowerSetting
{
get { return mPower; }
set { mPower = value; }
}
}
<Robot Name="Tony Stark" xmlns="https://ludovic.chabant.com/xaml">
<Robot.Engine>
<CustomizableEngine PowerSetting="40" />
</Robot.Engine>
<Robot.Weapon>
<MachineGunWeapon />
</Robot.Weapon>
</Robot>
We could even imagine that the built-in engine types can be customized too, but if you want the default values, you can use the simpler syntax.
The WPF designers ran into the same problem for things like brushes and colours, for which they wanted you to be able to just write “Red” and “Green” and “DarkOlive”, and have the appropriate graphic object be created. To this end, they created the ValueSerializer class. This class is a bit similar to TypeConverter, but only converts to and from strings. The big difference, however, is that the object to convert is passed as a parameter to the CanConvertTo method, which is not the case with the TypeConverter.
public class EngineValueSerializer : ValueSerializer
{
public override bool CanConvertToString(object value, IValueSerializerContext context)
{
if (value is NuclearEngine ||
value is ElectricEngine ||
value is CoalEngine)
return true;
return base.CanConvertToString(value, context);
}
public override string ConvertToString(object value, IValueSerializerContext context)
{
return value.GetType().Name.Replace("Engine", "");
}
}
Similarly to the TypeConverter, you need to decorate either the type or the property with the ValueSerializerAttribute. You can even leave the TypeConverterAttribute because the XAML serializer will give priority to the ValueSerializer.
[TypeConverter(typeof(EngineTypeConverter))]
[ValueSerializer(typeof(EngineValueSerializer))]
public abstract class Engine
{
public abstract int Power { get; }
}
Now we can say that if the object to convert is one of our 3 built-in types, we can convert it to a string which would be “Nuclear”, “Electric” or “Coal” (the type name without the “Engine” suffix). If it’s something else (like our CustomizableEngine), it won’t convert it, and revert back to the default XAML syntax, which uses XML elements. Obviously, for real code that has many built-in types, you will need to replace that ugly “if” statement by some lookup in a table, or inspecting the type for some custom attribute, or something.
Well, that’s all wonderful, and if we apply the same thing to the Weapon class, we can end up with some nice markup:
<Robot xmlns="https://ludovic.chabant.com/xaml"
Name="Tony Stark"
Engine="Nuclear"
Weapon="MachineGun" />
But if we want to customize the engine or the weapon, we need to switch to that verbose syntax:
<Robot Name="Tony Stark" xmlns="https://ludovic.chabant.com/xaml">
<Robot.Engine>
<CustomizableEngine PowerSetting="12" />
</Robot.Engine>
<Robot.Weapon>
<LaserWeapon BeamColor="Red" />
</Robot.Weapon>
</Robot>
In part 2 of this series, we’ll see how we can use the MarkupExtension type to keep some nicely short syntax even when specifying values for object properties. Stay tuned!