Serializing a NameValueCollection
I had a NameValueCollection embedded inside a larger object. I needed to serialize the larger object into XML and back. Unfortunately, NameValueCollection is not XML serializable. Why I do not know.
A blog comment from
Tim Erwin
got me started in the right direction. Implement
IXmlSerializable and do the work by hand in
ReadXml and WriteXml.
Tim's implementation turned out to be overly simple. It
didn't handle an empty collection well, nor did it leave the
XmlReader in a good state.
I used
SGen
to examine the deserialization of a
List<String> to figure out what else
needed to be done.
The following ReadXml seems to work. If I
expected to receive XML from untrusted sources, I would make
this more robust.
public void ReadXml(XmlReader reader)
{
if (reader.IsEmptyElement)
return;
while (reader.Read()
&& reader.NodeType != XmlNodeType.EndElement
&& reader.NodeType != XmlNodeType.None)
{
if (reader.NodeType == XmlNodeType.Element && reader.LocalName == "Header")
{
reader.MoveToAttribute("name");
string name = reader.Value;
reader.MoveToAttribute("value");
string value = reader.Value;
Add(name, value);
}
}
reader.ReadEndElement();
}
public void WriteXml(XmlWriter writer)
{
foreach (string name in nvc.Keys)
{
writer.WriteStartElement("Header");
string value = nvc[name];
writer.WriteAttributeString("name", name);
writer.WriteAttributeString("value", value);
writer.WriteEndElement();
}
}
public XmlSchema GetSchema( )
{
return null;
}
I also found that I needed to implement custom
Equals and GetHashCode, as the
NameValueCollection
implementations didn't seem to do what I wanted.
// Have to override GetHashCode() as two apparently identical NameValueCollections // will have different hash codes. public override int GetHashCode()
{
int hash = nvc.Count;
foreach (string name in nvc)
{
hash = 757 * hash + 101 * nvc[name].GetHashCode() + name.GetHashCode();
}
return hash;
}
public bool Equals(HeadersCollection that)
{
if (ReferenceEquals(that, null))
return false;
if (ReferenceEquals(this, that))
return true;
// Have to explicitly compare the contents of the collections // as NameValueCollection.Equals doesn't seem to do what we want. // Note: this is independent of order. if (nvc.Count != that.nvc.Count)
return false;
foreach (string name in nvc)
{
if (nvc[name] != that.nvc.Get(name))
return false;
}
return true;
}
public static bool Equals(HeadersCollection headersA, HeadersCollection headersB)
{
if (headersA == null)
return (headersB == null);
if (ReferenceEquals(headersA, headersB))
return true;
return headersA.Equals(headersB);
}
public override bool Equals(object obj)
{
if (obj is HeadersCollection)
return Equals((HeadersCollection) obj);
return false;
}