如何将Dictionary<string, object>序列化/反序列化为XML

5

我正在尝试为我的应用程序编写保存例程,其中应用程序的几个部分将项目添加到字典中,然后保存函数将它们写入XML文件。打开例程需要读取这些文件并重新填充字典,然后我可以将这些对象放回到我的应用程序中。我现在遇到反序列化的问题。我的保存例程如下:

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);

这个程序可以创建一个文件,但我希望将字典的键值也保存在文件中,但我不知道如何反序列化创建的文件,因为在读取对象之前我不知道它们的类型。
第二部分(我不喜欢在问题中添加新的部分,但我没有更好的方法来解决未来的问题)
现在我有以下代码:
var knownTypes = new List<Type>
{
   typeof(ObservableCollection<string>), 
   typeof(ObservableCollection<Segments>),
   typeof(Segments),
   typeof(List<string>)
};
var serialized = _SaveItems.Serialize(knownTypes);

但我遇到了以下异常

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.

如果我删除ObservableCollection或List的typeof,它就会抛出异常,表示它需要我删除的那个。

关于第二部分,我不确定这是一个框架限制还是错误 - xmamat
无论如何,我刚刚摆脱了我的List<string>,并在我的类中使用了ObservableCollection<string>,生活变得更容易了。感谢您对此的帮助。 - PlTaylor
1个回答

3
您可以使用 DataContractSerializer,如此帖子所述。但是,您可能需要将已知类型作为参数传递给序列化程序以支持嵌套的 object 类型类:
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);
    }
}

我的谷歌搜索能力在这里严重不足。昨天大部分时间我都在研究这个问题,但是要么没有找到答案,要么意义对我来说不明显。让我试一下,然后再回复你。 - PlTaylor
我在序列化时遇到了以下异常 - 类型 'MyProgram.InformationTabs.NavigationTrees.SitesViewModel',其数据合同名称为 'SitesViewModel:http://schemas.datacontract.org/2004/07/MyProgram.InformationTabs.NavigationTrees.' 未被预期。请考虑使用 DataContractResolver 或将任何静态未知类型添加到已知类型列表中 - 例如,通过使用 KnownTypeAttribute 属性或将它们添加到传递给 DataContractSerializer 的已知类型列表中。有没有关于如何正确装饰它的想法? - PlTaylor
你是对的:必须为序列化器提供已知类型,以便与“Dictionary<string, object>”一起使用。我已更新我的答案以满足您的要求。可能不是最优雅的方法,但我发现它能够工作。 - xmamat
我做了类似的事情,但仍然存在问题...我将更新我的原始问题,并添加第二部分以详细说明。 - PlTaylor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接