views:

1888

answers:

4

I'm trying to serialize a NameValueCollection over WCF. I keep getting exceptions telling me to add one type after another. After adding them, I finally get

Type 'System.Object[]' cannot be added to list of known types since another type 'System.Collections.ArrayList' with the same data contract name 'http://schemas.microsoft.com/2003/10/Serialization/Arrays:ArrayOfanyType' is already present.

The contract now looks like this:

[KnownType(typeof(NameValueCollection))]
[KnownType(typeof(CaseInsensitiveHashCodeProvider))]
[KnownType(typeof(CaseInsensitiveComparer))]
[KnownType(typeof(string[]))]
[KnownType(typeof(Object[]))]
[KnownType(typeof(ArrayList))]
[DataContract]
public class MyClassDataBase
{
    [DataMember]
    public NameValueCollection DataCollection = new NameValueCollection();
}

I really dont know what to do to be able to serialize my NameValueColletion.

Another strange thing is that the compiler warns that the CaseInsensitiveHashCodeProvider is deprecated.

+3  A: 

The NameValueCollection doesn't seem exactly to be designed for this use case. There is a series of blog posts on this blog that deals with this problem and presents a possible solution (to wit: use an IDictionary). I haven't tested it though.

dtb
+1  A: 

NameValueCollection does not directly implement the ICollection interface. Instead, NameValueCollection extends NameObjectCollectionBase. This implements the ICollection interface, and the overloaded Add(system.string) method is not implemented in the NameValueCollection class. When you use the XMLSerializer, the XmlSerializer tries to serialize or deserialize the NameValueCollection as a generic ICollection. Therefore, it looks for the default Add(System.String). In the absence of the Add(system.String) method, the exception is thrown.

Use the SoapFormatter class for serialization instead of using XML Serialization. To use the SoapFormatter class, you must add a reference to System.Runtime.Serialization.Formatters.Soap.dll.

// Serializing NameValueCollection object by using SoapFormatter
SoapFormatter sf = new SoapFormatter();
Stream strm1 = File.Open(@"C:\datasoap.xml", FileMode.OpenOrCreate,FileAccess.ReadWrite);
sf.Serialize(strm1,namValColl);
strm1.Close();

// Deserializing the XML file into NameValueCollection object
// by using SoapFormatter
SoapFormatter sf1 = new SoapFormatter();
Stream strm2 = File.Open(@"C:\datasoap.xml", FileMode.Open,FileAccess.Read);
NameValueCollection namValColl1 = (NameValueCollection)sf1.Deserialize(strm2);
strm2.Close();
Aaron
Thanks, I solved the problem by using the Dictionary<string, string> instead, which was much easier to use for me in this case.
kaze
ew, no, don't use the SoapFormatter. Interoperability will break.
Cheeso
@Kaze, then the solution you used is actually what Saunders proposed. Though you marked this answer "accepted", you actually used a different answer.
Cheeso
That is true, I've corrected this now.
kaze
+4  A: 

The best idea would be to stop using weak types like NameValueCollection and ArrayList. Use Dictionary and List instead.

John Saunders
NameValueCollection is weakly typed?
ironsam
I used the term "weak type", not "weakly typed". By this, I mean to include `NameValueCollection` in the set of collection types introduced before generics.
John Saunders
+1  A: 

Another approach could be to use a wrapper class to adapt NameValueCollection to WCF serialization. Here's a simple example that serializes each name-value pair as a single, comma-delimited string. It then reads that value back into the NameValueCollection upon deserialization:

[CollectionDataContract]
public class NameValueCollectionWrapper : IEnumerable 
{
    public NameValueCollectionWrapper() : this(new NameValueCollection()) { }

    public NameValueCollectionWrapper(NameValueCollection nvc) 
    {
        InnerCollection = nvc;
    }

    public NameValueCollection InnerCollection { get; private set; }

    public void Add(object value) 
    {
        var nvString = value as string;
        if (nvString != null) 
        {
            var nv = nvString.Split(',');
            InnerCollection.Add(nv[0], nv[1]);
        }
    }

    public IEnumerator GetEnumerator() 
    {
        foreach (string key in InnerCollection) 
        {
            yield return string.Format("{0},{1}", key, InnerCollection[key]);
        }
    }
}

This would allow you to use the type as a standard [DataMember] property on your DataContracts and you would also use standard WCF serialization techniques.

Ray Vernagus