Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to serialize/de-serialize a Dictionary<string, object> into XML

Tags:

c#

xml

I am trying to write a save routine for my application where several parts of the application add items to a Dictionary and then the save function writes them to a XML file. The open routine needs to read those files and re-populate the Dictionary and I can then place those objects back into my application. I am struggling with the de-serialization of the routine I have now. My save routine is as follows

XmlDocument xmlDoc = new XmlDocument();

// Write down the XML declaration
XmlDeclaration xmlDeclaration = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", null);

// Create the root element
XmlElement rootNode = xmlDoc.CreateElement("TireStudy");
xmlDoc.InsertBefore(xmlDeclaration, xmlDoc.DocumentElement);
xmlDoc.AppendChild(rootNode);

foreach (var saveItem in _SaveItems)
{
    XPathNavigator nav = rootNode.CreateNavigator();
    using (var writer = nav.AppendChild())
    {
        var serializer = new XmlSerializer(saveItem.Value.GetType());
        writer.WriteWhitespace("");
        serializer.Serialize(writer, saveItem.Value);
        writer.Close();
    }
}

xmlDoc.Save(fileName);

This routine works to create a file, but I would like the key value of the dictionary to be saved in the file as well and I am not sure how to de-serialize the file this creates because I do not know the types of the objects before I read them.

Part 2 (I hate adding new parts to a question, but I don't see a better way to address the problems going forward)

I now have the following code,

var knownTypes = new List<Type>
{
   typeof(ObservableCollection<string>), 
   typeof(ObservableCollection<Segments>),
   typeof(Segments),
   typeof(List<string>)
};
var serialized = _SaveItems.Serialize(knownTypes);

but I get the following exception

Type 'System.Collections.Generic.List`1[System.String]' cannot be added to list of known types since another type 'System.Collections.ObjectModel.ObservableCollection`1[System.String]' with the same data contract name 'http://schemas.microsoft.com/2003/10/Serialization/Arrays:ArrayOfstring' is already present. If there are different collections of a particular type - for example, List<Test> and Test[], they cannot both be added as known types.  Consider specifying only one of these types for addition to the known types list.

If I delete either the typeof(ObservableCollection) or the typeof(List) it exceptions complaining it needs the one I deleted.

like image 766
PlTaylor Avatar asked Apr 08 '14 11:04

PlTaylor


1 Answers

You could use DataContractSerializer as explained in this post but you may have to pass the known types as a parameter to the serializer to support nested object typed classes:

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

public static class SerializationExtensions
{
    public static string Serialize<T>(this T obj, IEnumerable<Type> knownTypes)
    {
        var serializer = new DataContractSerializer(obj.GetType(), knownTypes);
        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, IEnumerable<Type> knownTypes)
    {
        var serializer = new DataContractSerializer(typeof(T), knownTypes);
        using (var reader = new StringReader(serialized))
        using (var stm = new XmlTextReader(reader))
        {
            return (T)serializer.ReadObject(stm);
        }
    }
}

public class Address
{
    public string Country { get; set; }
    public string City { get; set; }
}
public class CodedAddress
{
    public int CountryCode { get; set; }
    public int CityCode { get; set; }
}
class Program
{
    static void Main(string[] args)
    {
        var persons1 = new Dictionary<string, Address>();
        persons1.Add("John Smith", new Address { Country = "US", City = "New York" });
        persons1.Add("Jean Martin", new Address { Country = "France", City = "Paris" });

        // no need to provide known types to the serializer
        var serializedPersons1 = persons1.Serialize(null);
        var deserializedPersons1 = serializedPersons1.Deserialize<Dictionary<string, Address>>(null);


        var persons2 = new Dictionary<string, object>();
        persons2.Add("John Smith", new Address { Country = "US", City = "New York" });
        persons2.Add("Jean Martin", new CodedAddress { CountryCode = 33, CityCode = 75 });

        // must provide known types to the serializer
        var knownTypes = new List<Type> { typeof(Address), typeof(CodedAddress) };
        var serializedPersons2 = persons2.Serialize(knownTypes);
        var deserializedPersons2 = serializedPersons2.Deserialize<Dictionary<string, object>>(knownTypes);
    }
}
like image 64
xmamat Avatar answered Sep 18 '22 02:09

xmamat