Serializing a NameValueCollection

content/binary/serialize-nvc.jpg

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;
}

8 Comments

  • This really depends a little on whether you need the underlying hashtable which is the part of the implementation of NameValueCollection that is giving you serialization problems. If your collection is small you are just as well of creating your own serializable NameValue type (i.e. contains string and object), and making a generic collection of them i.e. Collection which you can then serialize, such as:

    XmlSerializer xser = new XmlSerializer(typeof(Collection));
    StringWriter stringWriter = new StringWriter();
    xser.Serialize(stringWriter, value);

    It's a lot less code, and the way to go if you don't need hashtable's lookup speed.

  • Why go to all this trouble?

    [Serializable] class Header
    {
    [XmlAttribute("name")] public string Name { ..
    [XmlAttribute("value")] public string Value { ..
    }

  • It seems to me that we can serialize a dictionary using a SOAPFormater.

  • I believe the implementation of Equals() is broken, with this being the offending line:

    if (nvc[name] != that.nvc.Get(name))

    I think it should be

    if (nvc[name] != that.nvc[name])

  • Howdy just wanted to offer you a quick heads up.
    The words in your content seem to be running off the screen
    in Safari. I’m not sure if this is really a format issue or something to do with
    browser compatibility then again I figured I’d post to let you be acquainted
    with. The layout look great though! Hope you discover the issue resolved soon.

    Kudos

  • This is my very first time i go to here. I discovered a great
    number of entertaining stuff in your blog site, particularly it is discussion.
    From your many feedback in your articles, I guess I am not the simply
    one possessing each of that the satisfaction here!
    Preserve up that the great operate.

  • Cool website pages… [...]we came across a cool site you to might enjoy.
    Take a look whenever you want[...]……

  • text excellent - very good 2

Comments have been disabled for this content.