Deserializing OR'ed flags without the XmlSerializer

Most applications need to save state from time to time. On the full .NET Framework, this is quite easy, just use the XmlSerializer to serialize your state object. On the .NET Compact Framework, there is no public XmlSerializer (at least, not for version 1). What about using the XML DOM? If you are saving only a few values, loading an XmlDocument is overkill. In this week's tip, I'll show you how to use Reflection to work around this limitation. 

To keep the sample less cluttered, I have omitted the file I/O code, opting for a static array of strings.

When you ToString an enum object containing OR'ed flags, what you get looks like the elements of the testData array -- each selected value separated by a comma and a space. To decode this, you need to extract the elements and decode each one separately. Once decoded, you can OR them back together into the original value.

Using Reflection, this is quite simple, as you can see in the code below.

using System;

[Flags]
public enum TestEnum
{
Ice = 0x0001,
Marble = 0x0002,
Wood = 0x0004,
Clouds = 0x0008,
}

public class TestClass
{
public static void Main(string[] args)
{
String[] testData = new String[]{
"Marble",
"Ice, Wood, Marble, Clouds",
"Wood, Sand, Marble",
};

for(Int32 d = 0; d < testData.Length; d++)
{
Console.WriteLine();
Console.WriteLine(testData[d]);

// ToString() emits strings where the flag values are separated
// by ", ". Split on the ','. We'll fixup the spaces later.
String[] fields = testData[d].Split(',');

TestEnum te = (TestEnum)0;

// loop on each field name string
for(Int32 f = 0; f < fields.Length; f++)
{
// trim off the space (" ") left over from the call to Split()
String str = fields[f].Trim();

try
{
// find the field in the enum
System.Reflection.FieldInfo fi = typeof(TestEnum).GetField(str);

                    // get the value of the field and OR it with the
// current value
te |= (TestEnum)fi.GetValue(null);
}
catch
{
// enum does not contain a field by this name
// do not modify te
Console.WriteLine(String.Format("WARNING: TestEnum does not contain a {0} field", fields[f]));
}
}

Console.WriteLine(String.Format("Field value: {0} ({1})", te.ToString(), (Int32)te));
}

}
}

The code that is most interesting is within the try block.

// find the field in the enum
System.Reflection.FieldInfo fi = typeof(TestEnum).GetField(fields[f].Trim());

typeof(TestEnum) returns a Type object for the TestEnum enum. From that Type object, you can fetch a FieldInfo object (in the System.Reflection namespace) for the specified text. Once you have that FieldInfo object, you can get the field's value.

// get the value of the field and OR it with the
// current value
te |= (TestEnum)fi.GetValue(null);

If the field name doesn't exist in the target enum ("Sand" in the source example), GetValue() will throw a NullReferenceException.

The code above can be cut|pasted and built against either NetCF or the full .NET Framework. The output is shown below.

Marble
Field value: Marble (2)

Ice, Wood, Marble, Clouds
Field value: Ice, Marble, Wood, Clouds (15)

Wood, Sand, Marble
WARNING: TestEnum does not contain a Sand field
Field value: Marble, Wood (6)

Hope you find this useful.
-- DK

Disclaimer:
This posting is provided "AS IS" with no warranties, and confers no rights.