5

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.

PlTaylor
  • 7,345
  • 11
  • 52
  • 94
  • Regarding part 2, I'm not sure if this a framework [constraint](http://msdn.microsoft.com/en-us/library/aa347850.aspx) or [bug](http://support.microsoft.com/kb/967339). – xmamat Apr 08 '14 at 16:05
  • Either way, I just got rid of my List and used ObservableCollection in my class and life got easier. Thanks for your help on this. – PlTaylor Apr 08 '14 at 18:12

1 Answers1

3

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);
    }
}
QA Collective
  • 2,222
  • 21
  • 34
xmamat
  • 181
  • 5
  • My google-fu is seriously coming up short here. I spent the better part of yesterday looking into this and did not come up with that or the significance was lost on me. Let me try this out and get back to you. – PlTaylor Apr 08 '14 at 12:13
  • I am getting the following exception on serialization- Type 'MyProgram.InformationTabs.NavigationTrees.SitesViewModel' with data contract name 'SitesViewModel:http://schemas.datacontract.org/2004/07/MyProgram.InformationTabs.NavigationTrees.' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer. Any ideas on how to decorate this properly? – PlTaylor Apr 08 '14 at 12:33
  • You're right: one must provide the known types for the serializer to work with `Dictionary`. I've updated my answer to meet your requirement. Probably not the most elegant way to do it, but I found it to work. – xmamat Apr 08 '14 at 14:09
  • I did something similar and am still having problems...I will update my original question with a second part to detail it out. – PlTaylor Apr 08 '14 at 14:10