A Serializable KeyValuePair Class
Having accepted that Returning DataSets from WebServices is the Spawn of Satan and Represents All That Is Truly Evil in the World (or at least, not exactly best practice), I’ve been trying to make a conscious effort to instead use lightweight custom objects in my middle tier, to be exposed via web services for use by other applications. I felt sure that some of the new Generic classes in the .NET 2.0 FCL would help me in my quest. Specifically, as I often need to expose simple arrays of Key/Value Pair metadata, I planned to create an instance of the System.Collections.ObjectModel.Collection class, containing a bunch of System.Collections.Generic.KeyValuePair objects.
Well, this all seemed to go to plan, and worked just fine within my app. But when I exposed the method as a web service, I discovered that neither the Key nor the Value properties were serialized – I was left to consume a lovely generic collection of objects with no members – not a whole lot of use.
A little Googling turned up this blog entry, which explains that the Key and Value properties on the KeyValuePair class are both read-only, and by design the XmlSerializer will not serialize properties that don’t have a set accessor!
Damned if that little matter was going to stop me, I whipped out my copy of Lutz Roeder’s reflector to reverse engineer the KeyValuePair class (to be honest, I could have guessed most of it), and added a private set accessor to both properties, thus persuading the XmlSerializer to, well, do some serializing
Here’s the code for this new class:
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace IanFNelson
{
/// <summary>
/// It's just like a System.Collections.Generic.KeyValuePair,
/// but the XmlSerializer will serialize the
/// Key and Value properties!
/// </summary>
[Serializable, StructLayout( LayoutKind.Sequential )]
public struct KeyValuePairThatSerializesProperly<TKey, TValue>
{
private TKey key;
private TValue value;
public KeyValuePairThatSerializesProperly(TKey key,
TValue value)
{
this.key = key;
this.value = value;
}
public override string ToString()
{
StringBuilder builder1 = new StringBuilder();
builder1.Append( '[' );
if ( this.Key != null )
{
builder1.Append( this.Key.ToString() );
}
builder1.Append( ", " );
if ( this.Value != null )
{
builder1.Append( this.Value.ToString() );
}
builder1.Append( ']' );
return builder1.ToString();
}
/// <summary>
/// Gets the Value in the Key/Value Pair
/// </summary>
public TValue Value
{
get
{
return this.value;
}
set
{
throw new NotSupportedException();
}
}
/// <summary>
/// Gets the Key in the Key/Value pair
/// </summary>
public TKey Key
{
get
{
return this.key;
}
set
{
throw new NotSupportedException();
}
}
}
}
Having done that, to add a little clarity when using this class in the way I anticipate and eliminate repeated code, I decided to create some specific versions of the Collection, ReadOnlyCollection and KeyedCollection classes:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace IanFNelson
{
[Serializable()]
public class KeyValuePairCollection<TKey, TValue> :
Collection<KeyValuePairThatSerializesProperly<TKey, TValue>>
{
public void Add(TKey key, TValue value)
{
this.Add( new KeyValuePairThatSerializesProperly<TKey,
TValue>( key, value ) );
}
}
}
using System;
using System.Collections.ObjectModel;
using System.Collections;
using System.Collections.Generic;
namespace IanFNelson
{
[Serializable()]
public class ReadOnlyKeyValuePairCollection<TKey, TValue> :
ReadOnlyCollection<KeyValuePairThatSerializesProperly<TKey, TValue>>
{
public ReadOnlyKeyValuePairCollection(
IList<KeyValuePairThatSerializesProperly
<TKey, TValue>> list) :
base( list ) { }
}
}
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace IanFNelson
{
[Serializable()]
public class KeyValuePairKeyedCollection<TKey, TValue> :
KeyedCollection<TKey, KeyValuePairThatSerializesProperly
<TKey, TValue>>
{
protected override TKey GetKeyForItem(
KeyValuePairThatSerializesProperly<TKey, TValue> item)
{
return item.Key;
}
public void Add(TKey key, TValue value)
{
this.Add( new KeyValuePairThatSerializesProperly<TKey,
TValue>( key, value ) );
}
}
}









Thanks you have saved my ..!!! we had bunch of hashcollection which were not serializable and we had to pass through webservice. we have this code and starting point.
Can such a webservice be consumed? After all through the NotSupportedException the class cannot be deserialized. Or am I missing something?
First THANKS!
Remas is actually right. To allow deserialization change
throw new NotSupportedException();
for
this.value = value;
and
this.key = value;
and you will be good to go!
Martin, Remas:
Hey, I never claimed it was DEserializable!
Thanks for pointing that out!
Hi Ian. Great bit of code. I’m having trouble getting it to work on an array of SerializableDicts. It fails when it tries to execute the last line of:
fdt.UserParameters = new KeyValuePairCollection<string, string>[1];
fdt.UserParameters[0] = new KeyValuePairCollection<string, string>();
fdt.UserParameters[0].Add("hh", "ooo");
XmlSerializer ser = new XmlSerializer(typeof(FDTRequestorTransport));
Is there any obvious reason why this can’t work with arrays?
Thanks
Jerry
Thanks,
I initially forgot that KeyValuePair did not serialize, I got nulls on the other side. Now everything’s fine.
devMomentum
Thanks,
Can i use it with a List in WCF? like List<KeyValuePairThatSerializesProperly<string,string>> ?
I tried to use you’re idea of making the set properties throw an exception, but i get that exception back when i try to deserialize. Did it work for you?
@Dan – no, it didn’t. See the comments above from Martin and Remas.