5

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.

kaze
  • 4,299
  • 12
  • 53
  • 74

4 Answers4

9

The best idea would be to stop using weak types like NameValueCollection and ArrayList. Use Dictionary<string,string> and List<T> instead.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
  • NameValueCollection is weakly typed? – ironsam Sep 21 '09 at 22:02
  • 1
    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 Sep 24 '09 at 19:25
  • When I use Dictionaries in WCF they don't de-serialize very well. Or something is off when I deserialize it. – micahhoover Nov 14 '13 at 18:34
  • 2
    @micahhoover you will never be able to cleanly serialize all .NET types. The SOAP infrastructure (WSDL and XSD) cannot describe all of the semantics of every class. Also, I edited the post so you can see that I meant to use concrete generic types. For instance, `ArrayList` is pretty much the same as `List`, which gives the caller no information about what kinds of object are in the list. – John Saunders Nov 14 '13 at 19:42
  • [DataContractSerializer][1] serializes a Dictionary into JSON unexpectedly as an array of key-value pairs. As of .NET Framework 4.5, [DataContractJsonSerializerSettings][2] can tweak the behavior to serialize correctly, but there seems to be no easy way to make the default WCF serializer honor it. [1] https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.datacontractserializer?source=recommendations [2] https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.json.datacontractjsonserializersettings?redirectedfrom=MSDN&view=netframework-4.5 – Suncat2000 May 11 '23 at 19:02
4

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.

Cheeso
  • 189,189
  • 101
  • 473
  • 713
dtb
  • 213,145
  • 36
  • 401
  • 431
3

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
  • 6,130
  • 1
  • 24
  • 19
1

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
  • 2,427
  • 4
  • 30
  • 41
  • Thanks, I solved the problem by using the Dictionary instead, which was much easier to use for me in this case. – kaze Jun 02 '09 at 13:20
  • ew, no, don't use the SoapFormatter. Interoperability will break. – Cheeso Jun 02 '09 at 15:55
  • 1
    @Kaze, then the solution you used is actually what Saunders proposed. Though you marked this answer "accepted", you actually used a different answer. – Cheeso Jun 02 '09 at 16:02