Fun with IXmlSerializable
Have you ever tried to use .NET Xml Serialization with a Hashtable or Dictionary only to discover that you get a fun error that says you can’t serialize objects that implement IDictionary? I wasn’t too thrilled. I think I’m spoiled, because these days I’m addicted to using XmlSerializer and it usually just works. Until now.
All I wanted was to store a quick little XML file structured like so: (though a lot larger)
<?xml version="1.0" encoding="utf-8" ?> <Fields> <Field> <Position>1</Position> <Name>Account Number</Name> <Required>true</Required> </Field> <Field> <Position>2</Position> <Name>Address Line 1</Name> <Required>true</Required> </Field> <Field> <Position>3</Position> <Name>Address Line 2</Name> <Required>false</Required> </Field> <Field> <Position>4</Position> <Name>City</Name> <Required>true</Required> </Field> <Field> <Position>5</Position> <Name>State</Name> <Required>true</Required> </Field> <Field> <Position>6</Position> <Name>Zip</Name> <Required>true</Required> </Field> </Fields>
Then I wanted to read it into a class that had a generic Dictionary. The key would be the position, and the value would be an object that stores the name and required fields (there are actually a couple more, but I’m keeping it simple here). Honestly, should that be so hard? I want to be able to find the field at any position easily using the position as the key.
Here’s what I came up with. It works for this class, but since I’m overriding ReadXml and WriteXml, it obviously isn’t very extensible or pretty. Also, technically IXmlSerializable isn’t for public consumption, though I think enough people have cried that it might be fine to use now. In my opinion, this is rather breakable. I can’t say I’d use it for much more than this, but maybe someone else will get some use or a hint for their own utility class here.
This is fully testable code, as long as you change the file path and have the xml file there. There is no error trapping or anything, this code is just to demo the concept. Have fun!
using System;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using System.IO;
using System.Text;
using System.Diagnostics;
namespace ConsoleApplication1
{
public class Program
{
public class Field
{
public string Name;
public string Required;
}
public class Fields : IXmlSerializable
{
public Dictionary<int, Field> fieldList;
public Fields()
{
fieldList = new Dictionary<int, Field>();
}
public void WriteXml(XmlWriter writer)
{
writer.WriteRaw(Environment.NewLine);
foreach (int key in fieldList.Keys)
{
writer.WriteStartElement("Field");
writer.WriteRaw(Environment.NewLine);
writer.WriteStartElement("Position");
writer.WriteString(key.ToString());
writer.WriteEndElement();
writer.WriteRaw(Environment.NewLine);
writer.WriteStartElement("Name");
writer.WriteString(fieldList[key].Name);
writer.WriteEndElement();
writer.WriteRaw(Environment.NewLine);
writer.WriteStartElement("Required");
writer.WriteString(fieldList[key].Required);
writer.WriteEndElement();
writer.WriteRaw(Environment.NewLine);
writer.WriteEndElement();
writer.WriteRaw(Environment.NewLine);
}
}
public void ReadXml(XmlReader reader)
{
// start with the first main node
XmlNodeType type = reader.MoveToContent();
if (type == XmlNodeType.Element && reader.LocalName == "Fields")
{
// read in each Field element
reader.ReadStartElement("Fields");
while (type == XmlNodeType.Element && reader.LocalName == "Field")
{
reader.ReadStartElement("Field");
Field field = new Field();
int position = reader.ReadElementContentAsInt("Position", "");
field.Name = reader.ReadElementContentAsString("Name", "");
field.Required = reader.ReadElementContentAsString("Required", "");
fieldList.Add(position, field);
reader.ReadEndElement();
}
}
}
public XmlSchema GetSchema()
{
return (null);
}
}
static void Main(string[] args)
{
// let's take it for a test drive with no file
// using the debug output console to display the XML string
Trace.Listeners.Clear();
Trace.Listeners.Add(new DefaultTraceListener());
// construct an object
Fields fields = new Fields();
Field field = new Field();
field.Name = "Account Number";
field.Required = "true";
fields.fieldList.Add(1, field);
Field field2 = new Field();
field2.Name = "Address Line 1";
field2.Required = "true";
fields.fieldList.Add(2, field2);
// serialize it out to the debug window
XmlSerializer serializer = new XmlSerializer(typeof(Fields));
StringBuilder sb = new StringBuilder();
serializer.Serialize(new StringWriter(sb), fields);
Debug.WriteLine(sb.ToString());
// now read it back in (set a breakpoint to verify it reads it)
Fields fields2 = serializer.Deserialize(new StringReader(sb.ToString())) as Fields;
// let's try it with the file now (set a breakpoint to verify it reads it)
Fields fields3 = serializer.Deserialize(new StringReader(File.ReadAllText(@"C:\XMLFile1.xml"))) as Fields;
// just for giggles
Debug.WriteLine(fields3.fieldList[3].Name);
}
}
}
Tags: csharp, IXmlSerializable, XML Serialization
You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.