Why isn't there an XML-serializable dictionary in .NET?
Asked Answered
T

8

73

I need an XML-serializable dictionary. Actually, I now have two quite different programs that need one. I was rather surprised to see that .NET doesn't have one.

Can someone enlighten me, given how dependent various .NET features are on XML serialization, why there isn't an XML-serializable dictionary?

Trenchant answered 14/7, 2009 at 10:35 Comment(3)
The question is incorrect, because it gets cause and effect wrong. It should be, "why XmlSerializer cannot serialize dictionaries"? Because there are many ways to do XML serialization in .NET, and most of them serialize dictionaries just fine (DataContractSerializer, SoapFormatter ...).Poop
I'm guessing you haven't examined "XmlDictionaryWriter.CreateDictionaryWriter"... or the 100 other ways to serialize dictionaries in .NET (some of them are built-in). ...Also why do you need a dictionary? I've always found strong typed objects to work better, why not just implement a class with a [DataContract], and IExtensibleDataObject?Feder
Which modern features in .NET do you think are dependent on XML Serialization? Config files do not use serialization, and ASMX web services are only meant for legacy use. (moved to comment from answer)Waynant
W
15

The thing about XML Serialization is that it's not just about creating a stream of bytes. It's also about creating an XML Schema that this stream of bytes would validate against. There's no good way in XML Schema to represent a dictionary. The best you could do is to show that there's a unique key.

You can always create your own wrapper, for instance One Way to Serialize Dictionaries.

Waynant answered 14/7, 2009 at 10:39 Comment(12)
My two cases are web services and configuration files. So, you're saying that the .NET Framework guys were limited by a deficiency in the XML Schema specification? I have found stuff online but using a built-in class in a lot less work than deciding if someone else has done it right. I'll have a look at the one you suggested.Trenchant
ASMX web services are now considered legacy technology. See johnwsaundersiii.spaces.live.com/blog/…. There's an entire API for configuration files - it doesn't use XML Serialization. Anything else?Waynant
BTW, the "limitation" is a design decision. As you say, it was used for web services - but not just to serialize and deserialize - it's what produced the schemas that are part of the WSDL. It's all part of a whole, and it all has to work together.Waynant
I know they're legacy but that doesn't mean that I am going to be given the time to learn WCF. Someone noted that software shouldn't be gold-plated, it should do the job. ASMX does the job. The pace of Microsoft's development of .NET is exciting and wonderful but out of touch with the current market: training budgets slashed, cutting back, only doing work that MUST be done. The non-IT parts of the business look askance when we say "We need to upgrade because Microsoft won't be supporting technology X any more". (I know it's not just MS but it is OFTEN MS.) So I'm stuck with ASMX for now.Trenchant
You said that "given how dependent various .NET features are on XML serialization" you couldn't understand why there wasn't one. I said there are few features of .NET dependent on XML Ser. You mentioned ASMX and Config. I said ASMX is legacy and config doesn't use XML Ser. "Legacy" was meant to show why they'd be in no hurry to add dictionary support. Also, see johnwsaundersiii.spaces.live.com/blog/….Waynant
I concede the point on config - it was a while ago - I've clearly mis-remembered the problem. I've just found a series of articles on CodeProject about the Config API and I'm looking forward to getting my teeth into them. Your original reply answered my question. Thank you. I was just proving an alternative view to your 'legacy' point. I love .NET. I just wish MS would slow down.Trenchant
But, life doesn't slow down. WCF was introduced almost three years ago, with the clear intention of replacing ASMX, WSE and Remoting. Microsoft is only now beginning to publicly acknowledge that, and I think it's past time.Waynant
I suppose not. I'm surprised it's been 3 years: doesn't seem like it. I teach myself a lot in my own time but I've always been put off WCF by what might just be my perception that it's complicated and a lot more work to set up than ASMX. It'd be nice if MS would divert some of their .NET energy to increasing the coverage of .NET over Win32 so I didn't have to Interop or P/Invoke so much. But in the same way that I have to be able to justify the non-productive periods whilst I'm learning new stuff, they have to be able to sell new versions of VS, don't they?Trenchant
Take 5 minutes when you get a chance and see johnwsaundersiii.spaces.live.com/blog/…. It's got lots of pictures, for those who, unlike you, don't get the concepts of web services. But it happens to include a quick run-through (or run-past) of a trivial WCF service and client. Also, msdn.microsoft.com/en-us/netframework/dd939784.aspx for good "lunch time" video viewing.Waynant
@John, how do you know that the limitation is a design decision? It's the first time I hear anyone say that.Poop
@Pavel: "limitation" was in the context of the comment from the OP: "limited by a deficiency in the XML Schema specification". My understanding is that the design decision was to produce XML that can be described by XML Schema.Waynant
And still, SharePoint decides to serialize web part properties using the XmlSerializer. Sigh .Tellez
L
53

I know this has been answered before, but since I have a very concise way (code) for doing IDictionary serialization with the DataContractSerializer class (used by WCF, but could and should be used anywhere) I couldn't resist contributing it here:

public static class SerializationExtensions
{
    public static string Serialize<T>(this T obj)
    {
        var serializer = new DataContractSerializer(obj.GetType());
        using (var writer = new StringWriter())
        using (var stm = new XmlTextWriter(writer))
        {
            serializer.WriteObject(stm, obj);
            return writer.ToString();
        }
    }
    public static T Deserialize<T>(this string serialized)
    {
        var serializer = new DataContractSerializer(typeof(T));
        using (var reader = new StringReader(serialized))
        using (var stm = new XmlTextReader(reader))
        {
            return (T)serializer.ReadObject(stm);
        }
    }
}

This works perfectly in .NET 4 and should also work in .NET 3.5, although I didn't test it yet.

UPDATE: It doesn't work in .NET Compact Framework (not even NETCF 3.7 for Windows Phone 7) as the DataContractSerializer is not supported!

I did the streaming to string because it was more convenient to me, although I could have introduced a lower-level serialization to Stream and then used it to serialize to strings, but I tend to generalize only when needed (just like premature optimization is evil, so it is premature generalization...)

Usage is very simple:

// dictionary to serialize to string
Dictionary<string, object> myDict = new Dictionary<string, object>();
// add items to the dictionary...
myDict.Add(...);
// serialization is straight-forward
string serialized = myDict.Serialize();
...
// deserialization is just as simple
Dictionary<string, object> myDictCopy = 
    serialized.Deserialize<Dictionary<string,object>>();

myDictCopy will be a verbatim copy of myDict.

You'll also notice that the generic methods provided will be able to serialize any type (to the best of my knowledge) since it is not limited to IDictionary interfaces, it can be really any generic type T.

Hope it helps someone out there!

Lake answered 9/5, 2011 at 18:49 Comment(11)
Works great! To other developers: You'll need to add a project reference for System.Runtime.Serialization if you don't already have one, but it is available in the .NET 4.0 Client Profile.Tanhya
It did not work with Windows Phone 8 SDK targeting Windows Phone 7.5 (which is Silverlight 3).Incorporate
@Incorporate Per the DataContractSerializer documentation it supports the following platforms: Windows 8, Windows Server 2012, Windows 7, Windows Vista SP2, Windows Server 2008 (Server Core Role not supported), Windows Server 2008 R2 (Server Core Role supported with SP1 or later; Itanium not supported)... It doesn't mention the phone SDK... The Windows Phone 7 uses .NET Compact Framework 3.7, thus no DataContractSerializer :-( I've updated the post accordingly so people do not loose time figuring out what didn't work! Thanks Adarsha!Lake
I don't know why I didn't see this before, but -1 for using new XmlTextWriter and new XmlTextReader instead of XmlReader.Create and XmlWriter.Create.Waynant
@JohnSaunders why would I do that when I know already which XmlReader or Writer I want. Did you take a look at XmlWriter/Reader.Create? There is a heft penalty for calling it, and this method should be as fast as possible as it can be used in a tight loop serializing lots of objects (I concede I would use another serialization method if performance was the issue here though). But anyway, the recommended way is to use XmlWriter/Reader.Create, but since I'm programming .NET from the onset (version 1) I guess I'm used to do some things the "old" way.Lake
You should not use new XmlTextReader() or new XmlTextWriter(). They have been deprecated since .NET 2.0. Use XmlReader.Create() or XmlWriter.Create() instead.Waynant
@JohnSaunders they are NOT deprecated. It is SUGGESTED to use XmlWriter.Create "to take full advantages of the new features...". There's a clear distinction between a usage suggestion and a deprecated method.Lake
@Loudenvier: which feature of XmlTextWriter did you require? Your code could as easily have used XmlWriter.Create, and should have done so. As it stands, it's a bad example, and deserves the downvote.Waynant
@JohnSaunders you're sticking with the idea that XmlTextWriter constructor is deprecated, it isn't. The .NET team SUGGESTs the use of XmlWriter.Create "to take full advantages of bla bla bla".. I didn't need any of it. In fact, as it stands, there is not a single drawback to my implementation other than cosmetics. Your downvote makes not difference at all, but it is unfair, even more for the most helpful answer to the question in hand. Your conscience is your guide. I rest my case :-)Lake
@Loudenvier: I'm suggesting that I see no reason for you to use new XmlTextWriter and such, and that readers who copy your code will not know that you are simply being stubborn and/or nostalgic. Some will copy your code thinking that new XmlTextWriter is the correct way to create all XmlWriter instances. If you were using some feature that XmlTextWriter had that XmlWriter did not, then I would not have downvoted. But you're not, and you're going to confuse readers for years to come. Remember that an answer is not just for the OP - it's for all who read it later.Waynant
@JohnSaunders I'm not here to teach people how to create XmlWriters! I was just showing a, correct by the way (despite your reservations), method of serializing a Dictionary in .NET. If you see ulterior, perhaps spiritual meanings in SO answers, that's up to you. I also believe you could have edited the answer to use XmlWrite, that would have been a lot more helpful to others, and also a lot more "synergetic" :-) (BTW: there're quite a few examples in MSN using new XmlTextWriter, go look for them...)Lake
W
15

The thing about XML Serialization is that it's not just about creating a stream of bytes. It's also about creating an XML Schema that this stream of bytes would validate against. There's no good way in XML Schema to represent a dictionary. The best you could do is to show that there's a unique key.

You can always create your own wrapper, for instance One Way to Serialize Dictionaries.

Waynant answered 14/7, 2009 at 10:39 Comment(12)
My two cases are web services and configuration files. So, you're saying that the .NET Framework guys were limited by a deficiency in the XML Schema specification? I have found stuff online but using a built-in class in a lot less work than deciding if someone else has done it right. I'll have a look at the one you suggested.Trenchant
ASMX web services are now considered legacy technology. See johnwsaundersiii.spaces.live.com/blog/…. There's an entire API for configuration files - it doesn't use XML Serialization. Anything else?Waynant
BTW, the "limitation" is a design decision. As you say, it was used for web services - but not just to serialize and deserialize - it's what produced the schemas that are part of the WSDL. It's all part of a whole, and it all has to work together.Waynant
I know they're legacy but that doesn't mean that I am going to be given the time to learn WCF. Someone noted that software shouldn't be gold-plated, it should do the job. ASMX does the job. The pace of Microsoft's development of .NET is exciting and wonderful but out of touch with the current market: training budgets slashed, cutting back, only doing work that MUST be done. The non-IT parts of the business look askance when we say "We need to upgrade because Microsoft won't be supporting technology X any more". (I know it's not just MS but it is OFTEN MS.) So I'm stuck with ASMX for now.Trenchant
You said that "given how dependent various .NET features are on XML serialization" you couldn't understand why there wasn't one. I said there are few features of .NET dependent on XML Ser. You mentioned ASMX and Config. I said ASMX is legacy and config doesn't use XML Ser. "Legacy" was meant to show why they'd be in no hurry to add dictionary support. Also, see johnwsaundersiii.spaces.live.com/blog/….Waynant
I concede the point on config - it was a while ago - I've clearly mis-remembered the problem. I've just found a series of articles on CodeProject about the Config API and I'm looking forward to getting my teeth into them. Your original reply answered my question. Thank you. I was just proving an alternative view to your 'legacy' point. I love .NET. I just wish MS would slow down.Trenchant
But, life doesn't slow down. WCF was introduced almost three years ago, with the clear intention of replacing ASMX, WSE and Remoting. Microsoft is only now beginning to publicly acknowledge that, and I think it's past time.Waynant
I suppose not. I'm surprised it's been 3 years: doesn't seem like it. I teach myself a lot in my own time but I've always been put off WCF by what might just be my perception that it's complicated and a lot more work to set up than ASMX. It'd be nice if MS would divert some of their .NET energy to increasing the coverage of .NET over Win32 so I didn't have to Interop or P/Invoke so much. But in the same way that I have to be able to justify the non-productive periods whilst I'm learning new stuff, they have to be able to sell new versions of VS, don't they?Trenchant
Take 5 minutes when you get a chance and see johnwsaundersiii.spaces.live.com/blog/…. It's got lots of pictures, for those who, unlike you, don't get the concepts of web services. But it happens to include a quick run-through (or run-past) of a trivial WCF service and client. Also, msdn.microsoft.com/en-us/netframework/dd939784.aspx for good "lunch time" video viewing.Waynant
@John, how do you know that the limitation is a design decision? It's the first time I hear anyone say that.Poop
@Pavel: "limitation" was in the context of the comment from the OP: "limited by a deficiency in the XML Schema specification". My understanding is that the design decision was to produce XML that can be described by XML Schema.Waynant
And still, SharePoint decides to serialize web part properties using the XmlSerializer. Sigh .Tellez
F
13

They added one in .NET 3.0. If you can, add a reference to System.Runtime.Serialization and look for System.Xml.XmlDictionary, System.Xml.XmlDictionaryReader, and System.Xml.XmlDictionaryWriter.

I would agree that it is not in a particularly discoverable place.

Forde answered 14/7, 2009 at 10:42 Comment(2)
These classes are not general-purpose serializable dictionaries. They're related to the implementation of serialization in WCF.Waynant
I don't understand the commment. Why AREN'T they general-purpose xml-serializable dictionaries? What part of "System.Xml.XmlDictionary" or "System.Runtime.Serialization" indicates non-genericness?Sternutatory
M
4

Create one of your own :-), the readonly feature is bonus but if you need a key other than a string then the class needs some modifications...

namespace MyNameSpace
{
    [XmlRoot("SerializableDictionary")]
    public class SerializableDictionary : Dictionary<String, Object>, IXmlSerializable
    {
        internal Boolean _ReadOnly = false;
        public Boolean ReadOnly
        {
            get
            {
                return this._ReadOnly;
            }

            set
            {
                this.CheckReadOnly();
                this._ReadOnly = value;
            }
        }

        public new Object this[String key]
        {
            get
            {
                Object value;

                return this.TryGetValue(key, out value) ? value : null;
            }

            set
            {
                this.CheckReadOnly();

                if(value != null)
                {
                    base[key] = value;
                }
                else
                {
                    this.Remove(key);
                }               
            }
        }

        internal void CheckReadOnly()
        {
            if(this._ReadOnly)
            {
                throw new Exception("Collection is read only");
            }
        }

        public new void Clear()
        {
            this.CheckReadOnly();

            base.Clear();
        }

        public new void Add(String key, Object value)
        {
            this.CheckReadOnly();

            base.Add(key, value);
        }

        public new void Remove(String key)
        {
            this.CheckReadOnly();

            base.Remove(key);
        }

        public XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(XmlReader reader)
        {
            Boolean wasEmpty = reader.IsEmptyElement;

            reader.Read();

            if(wasEmpty)
            {
                return;
            }

            while(reader.NodeType != XmlNodeType.EndElement)
            {
                if(reader.Name == "Item")
                {
                    String key = reader.GetAttribute("Key");
                    Type type = Type.GetType(reader.GetAttribute("TypeName"));

                    reader.Read();
                    if(type != null)
                    {
                        this.Add(key, new XmlSerializer(type).Deserialize(reader));
                    }
                    else
                    {
                        reader.Skip();
                    }
                    reader.ReadEndElement();

                    reader.MoveToContent();
                }
                else
                {
                    reader.ReadToFollowing("Item");
                }

            reader.ReadEndElement();
        }

        public void WriteXml(XmlWriter writer)
        {
            foreach(KeyValuePair<String, Object> item in this)
            {
                writer.WriteStartElement("Item");
                writer.WriteAttributeString("Key", item.Key);
                writer.WriteAttributeString("TypeName", item.Value.GetType().AssemblyQualifiedName);

                new XmlSerializer(item.Value.GetType()).Serialize(writer, item.Value);

                writer.WriteEndElement();
            }
        }

    }
}
Madelene answered 14/7, 2009 at 10:42 Comment(1)
There was a bug in this code -- if there was whitespace in the xml the reading could enter an infinite loop. I fixed this bug but there may be more.Mendacity
C
4

Use the DataContractSerializer! See the sample below.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.Xml;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            a.Value = 1;

            B b = new B();
            b.Value = "SomeValue";

            Dictionary<A, B> d = new Dictionary<A,B>();
            d.Add(a, b);
            DataContractSerializer dcs = new DataContractSerializer(typeof(Dictionary<A, B>));
            StringBuilder sb = new StringBuilder();
            using (XmlWriter xw = XmlWriter.Create(sb))
            {
                dcs.WriteObject(xw, d);
            }
            string xml = sb.ToString();
        }
    }

    public class A
    {
        public int Value
        {
            get;
            set;
        }
    }

    public class B
    {
        public string Value
        {
            get;
            set;
        }
    }
}

The above code produces the following xml:

<?xml version="1.0" encoding="utf-16"?>
<ArrayOfKeyValueOfABHtQdUIlS xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
    <KeyValueOfABHtQdUIlS>
        <Key xmlns:d3p1="http://schemas.datacontract.org/2004/07/ConsoleApplication1">
            <d3p1:Value>1</d3p1:Value>
        </Key>
        <Value xmlns:d3p1="http://schemas.datacontract.org/2004/07/ConsoleApplication1">
            <d3p1:Value>SomeValue</d3p1:Value>
        </Value>
    </KeyValueOfABHtQdUIlS>
</ArrayOfKeyValueOfABHtQdUIlS>
Courtroom answered 9/9, 2009 at 6:2 Comment(0)
E
2

A generic helper to quickly add IXmlSerializable to any (existing) Dictionary without using inheritance:

using System.Xml;
using System.Xml.Serialization;
using System.Collections.Generic;

namespace GameSpace {

    public class XmlSerializerForDictionary {

        public struct Pair<TKey,TValue> {

            public TKey Key;
            public TValue Value;

            public Pair(KeyValuePair<TKey,TValue> pair) {
                Key = pair.Key;
                Value = pair.Value;
            }//method

        }//struct

        public static void WriteXml<TKey,TValue>(XmlWriter writer, IDictionary<TKey,TValue> dict) {

            var list = new List<Pair<TKey,TValue>>(dict.Count);

            foreach (var pair in dict) {
                list.Add(new Pair<TKey,TValue>(pair));
            }//foreach

            var serializer = new XmlSerializer(list.GetType());
            serializer.Serialize(writer, list);

        }//method

        public static void ReadXml<TKey, TValue>(XmlReader reader, IDictionary<TKey, TValue> dict) {

            reader.Read();

            var serializer = new XmlSerializer(typeof(List<Pair<TKey,TValue>>));
            var list = (List<Pair<TKey,TValue>>)serializer.Deserialize(reader);

            foreach (var pair in list) {
                dict.Add(pair.Key, pair.Value);
            }//foreach

            reader.Read();

        }//method

    }//class

}//namespace

And a convenient serializable generic dictionary:

using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using System.Collections.Generic;

namespace GameSpace {

    public class SerializableDictionary<TKey,TValue> : Dictionary<TKey,TValue>, IXmlSerializable {

        public virtual void WriteXml(XmlWriter writer) {
            XmlSerializerForDictionary.WriteXml(writer, this);
        }//method

        public virtual void ReadXml(XmlReader reader) {
            XmlSerializerForDictionary.ReadXml(reader, this);
        }//method

        public virtual XmlSchema GetSchema() {
            return null;
        }//method

    }//class

}//namespace
Eijkman answered 7/5, 2012 at 12:7 Comment(0)
H
1

This is my implementation.

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.Xml;

namespace Rubik.Staging
{    
    [XmlSchemaProvider("GetInternalSchema")]
    public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
    {
        #region IXmlSerializable Members

        private const string ns = "http://www.rubik.com.tr/staging";

        public static XmlQualifiedName GetInternalSchema(XmlSchemaSet xs)
        {
            bool keyIsSimple = (typeof(TKey).IsPrimitive || typeof(TKey) == typeof(string));
            bool valueIsSimple = (typeof(TValue).IsPrimitive || typeof(TValue) == typeof(string));

            XmlSchemas schemas = new XmlSchemas();

            XmlReflectionImporter importer = new XmlReflectionImporter(ns);
            importer.IncludeType(typeof(TKey));            
            importer.IncludeType(typeof(TValue));            

            XmlTypeMapping keyMapping = importer.ImportTypeMapping(typeof(TKey));            
            XmlTypeMapping valueMapping = importer.ImportTypeMapping(typeof(TValue));          

            XmlSchemaExporter exporter = new XmlSchemaExporter(schemas); 

            if(!keyIsSimple)
                exporter.ExportTypeMapping(keyMapping);
            if(!valueIsSimple)
                exporter.ExportTypeMapping(valueMapping);

            XmlSchema schema = (schemas.Count == 0 ? new XmlSchema() : schemas[0]);

            schema.TargetNamespace = ns;          
            XmlSchemaComplexType type = new XmlSchemaComplexType();
            type.Name = "DictionaryOf" + keyMapping.XsdTypeName + "And" + valueMapping.XsdTypeName;
            XmlSchemaSequence sequence = new XmlSchemaSequence();
            XmlSchemaElement item = new XmlSchemaElement();
            item.Name = "Item";

            XmlSchemaComplexType itemType = new XmlSchemaComplexType();            
            XmlSchemaSequence itemSequence = new XmlSchemaSequence();

            XmlSchemaElement keyElement = new XmlSchemaElement();

            keyElement.Name = "Key";
            keyElement.MaxOccurs = 1;
            keyElement.MinOccurs = 1;

            XmlSchemaComplexType keyType = new XmlSchemaComplexType();
            XmlSchemaSequence keySequence = new XmlSchemaSequence();
            XmlSchemaElement keyValueElement = new XmlSchemaElement();
            keyValueElement.Name = keyMapping.ElementName;
            keyValueElement.SchemaTypeName = new XmlQualifiedName(keyMapping.XsdTypeName, keyMapping.XsdTypeNamespace);
            keyValueElement.MinOccurs = 1;
            keyValueElement.MaxOccurs = 1;
            keySequence.Items.Add(keyValueElement);
            keyType.Particle = keySequence;
            keyElement.SchemaType = keyType;
            itemSequence.Items.Add(keyElement);


            XmlSchemaElement valueElement = new XmlSchemaElement();

            valueElement.Name = "Value";
            valueElement.MaxOccurs = 1;
            valueElement.MinOccurs = 1;

            XmlSchemaComplexType valueType = new XmlSchemaComplexType();
            XmlSchemaSequence valueSequence = new XmlSchemaSequence();
            XmlSchemaElement valueValueElement = new XmlSchemaElement();
            valueValueElement.Name = valueMapping.ElementName;
            valueValueElement.SchemaTypeName = new XmlQualifiedName(valueMapping.XsdTypeName, valueMapping.XsdTypeNamespace);
            valueValueElement.MinOccurs = 1;
            valueValueElement.MaxOccurs = 1;
            valueSequence.Items.Add(valueValueElement);
            valueType.Particle = valueSequence;
            valueElement.SchemaType = valueType;
            itemSequence.Items.Add(valueElement);
            itemType.Particle = itemSequence;
            item.SchemaType = itemType;            
            sequence.Items.Add(item);
            type.Particle = sequence;
            schema.Items.Add(type);

            xs.XmlResolver = new XmlUrlResolver();
            xs.Add(schema);

            return new XmlQualifiedName(type.Name, ns);
        }





        public void ReadXml(System.Xml.XmlReader reader)
        {
            XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
            XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));

            bool wasEmpty = reader.IsEmptyElement;
            reader.Read();

            if (wasEmpty)
                return;

            while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
            {
                reader.ReadStartElement("Item");

                reader.ReadStartElement("Key");
                TKey key = (TKey)keySerializer.Deserialize(reader);
                reader.ReadEndElement();

                reader.ReadStartElement("Value");
                TValue value = (TValue)valueSerializer.Deserialize(reader);
                reader.ReadEndElement();

                this.Add(key, value);

                reader.ReadEndElement();

                reader.MoveToContent();
            }

            reader.ReadEndElement();
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
            XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));

            foreach (TKey key in this.Keys)
            {
                writer.WriteStartElement("Item");

                writer.WriteStartElement("Key");
                keySerializer.Serialize(writer, key);
                writer.WriteEndElement();

                writer.WriteStartElement("Value");
                TValue value = this[key];
                valueSerializer.Serialize(writer, value);
                writer.WriteEndElement();

                writer.WriteEndElement();
            }
        }

        #endregion

        #region IXmlSerializable Members

        public XmlSchema GetSchema()
        {
            return null;
        }

        #endregion
    }

}
Heartburning answered 21/2, 2011 at 23:31 Comment(1)
You should comment on the advantages of using your more verbose solution over those that were proposed elsewhere in this thread. It is not immediately clear why one should use this over a more simple implementation.Buyse
K
1

I know this has been done to death now, but here is my contribution. I took the good bits from the solutions from @Loudenvier and @Jack and wrote my own serialisable (sorry, I'm British) dictionary class.

public class SerialisableDictionary<T1, T2> : Dictionary<T1, T2>, IXmlSerializable
{
    private static DataContractSerializer serializer =
        new DataContractSerializer(typeof(Dictionary<T1, T2>));

    public void WriteXml(XmlWriter writer)
    {
        serializer.WriteObject(writer, this);
    }

    public void ReadXml(XmlReader reader)
    {
        Dictionary<T1, T2> deserialised =
            (Dictionary<T1, T2>)serializer.ReadObject(reader);

        foreach(KeyValuePair<T1, T2> kvp in deserialised)
        {
            Add(kvp.Key, kvp.Value);
        }
    }

    public XmlSchema GetSchema()
    {
        return null;
    }
}

I like this approach because you won't have to explicitly serialise or deserialise anything, just pump the whole class hierarchy through an XmlSerializer and you're done.

Kirst answered 8/12, 2015 at 10:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.