首页 > 解决方案 > 如何将具有私有字典成员的类序列化到文件中?

问题描述

我正在尝试编写将类型(Dispatcher如下所示的类型)保存到文件中的函数,然后稍后重新加载。我写了以下函数,但它们不起作用。我得到一个例外:

抛出异常:System.Xml.dll 中的“System.InvalidOperationException”

在其他地方我读到这可能是因为班级中的成员是私人的,那么我应该使用BinaryFormatter,但它没有用。

我究竟做错了什么 ?

Dispatcher该类将用于存储消息,也将允许用户从中提取消息。所以我想备份数据以防出错,然后能够重新加载它)。

public class Dispatcher
{
    private Dictionary<int, Dictionary<int, Dictionary<int, Message>>> m_DataBase;

    private Dispatcher()
    {
        m_DataBase = new Dictionary<int, Dictionary<int, Dictionary<int, Message>>>();
    }

    public static Dispatcher LoadFromFile()
    {
        Dispatcher loadedDataBase;

        try
        {
            using (Stream stream = new FileStream(@".\DispatcherDataBase.xml", FileMode.Open))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(Dispatcher));
                loadedDataBase = serializer.Deserialize(stream) as Dispatcher;
            }
        }
        catch (Exception)
        {
            loadedDataBase = new Dispatcher();
        }

        return loadedDataBase;
    }

    public void SaveToFile()
    {
        FileMode wantedFileModeForStream;

        try
        {
            if (File.Exists(@".\DispatcherDataBase.xml"))
            {
                wantedFileModeForStream = FileMode.Truncate;
            }
            else
            {
                wantedFileModeForStream = FileMode.CreateNew;
            }

            using (Stream stream = new FileStream(@".\DispatcherDataBase.xml", wantedFileModeForStream))
            {
                XmlSerializer serializer = new XmlSerializer(this.GetType());
                serializer.Serialize(stream, this);
            }
        }
        catch (Exception)
        {
        }
    }
}

标签: c#xmlserialization

解决方案


您正在尝试使用XmlSerializer序列化您的Dispatcher类型并遇到三个不同的问题:

  1. XmlSerializer要求您的类型具有公共无参数构造函数。

  2. XmlSerializer 不支持字典

  3. XmlSerializer不会序列化非公共成员,例如m_DataBase.

DataContractSerializer(以及DataContractJsonSerializer)没有这些限制。如果您使用数据协定属性Dispatcher标记您的类型,您将能够使用此序列化程序将其序列化为 XML。

因此,如果您按如下方式修改您的类型:

public static class Constants
{
    public const string DataContractNamespace = ""; // Or whatever
}

[DataContract(Name = "Dispatcher", Namespace = Constants.DataContractNamespace)]
public partial class Dispatcher
{
    [DataMember]
    private Dictionary<int, Dictionary<int, Dictionary<int, Message>>> m_DataBase;

    private Dispatcher()
    {
        m_DataBase = new Dictionary<int, Dictionary<int, Dictionary<int, Message>>>();
    }

    [System.Runtime.Serialization.OnDeserialized]
    void OnDeserializedMethod(System.Runtime.Serialization.StreamingContext context)
    {
        // Ensure m_DataBase is not null after deserialization (DataContractSerializer does not call the constructor).
        if (m_DataBase == null)
            m_DataBase = new Dictionary<int, Dictionary<int, Dictionary<int, Message>>>();
    }

    internal const string FileName = @"DispatcherDataBase.xml";

    public static Dispatcher LoadFromFile()
    {
        Dispatcher loadedDataBase;

        try
        {
            using (var stream = new FileStream(FileName, FileMode.Open))
            {
                var serializer = new DataContractSerializer(typeof(Dispatcher));
                loadedDataBase = serializer.ReadObject(stream) as Dispatcher;
            }
        }
        catch (Exception)
        {
            loadedDataBase = new Dispatcher();
        }

        return loadedDataBase;
    }

    public void SaveToFile()
    {
        using (var stream = new FileStream(FileName, FileMode.Create))
        using (var writer = XmlWriter.Create(stream, new XmlWriterSettings { Indent = true })) // Optional indentation for readability only.
        {
            var serializer = new DataContractSerializer(this.GetType());
            serializer.WriteObject(writer, this);
        }
    }
}

// Not shown in question, added as an example
[DataContract(Name = "Message", Namespace = Constants.DataContractNamespace)]
public class Message
{
    [DataMember]
    public string Value { get; set; }
}

您将能够将您的课程往返Dispatcher于 XML。演示小提琴#1在这里

或者,您可以DataContractJsonSerializer通过简单地交换序列化程序并消除可选的来使用并序列化为 JSON XmlWriter

    public static Dispatcher LoadFromFile()
    {
        Dispatcher loadedDataBase;

        try
        {
            using (var stream = new FileStream(FileName, FileMode.Open))
            {
                var serializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(Dispatcher));
                loadedDataBase = serializer.ReadObject(stream) as Dispatcher;
            }
        }
        catch (Exception)
        {
            loadedDataBase = new Dispatcher();
        }

        return loadedDataBase;
    }

    public void SaveToFile()
    {
        using (var stream = new FileStream(FileName, FileMode.Create))
        {
            var serializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(this.GetType());
            serializer.WriteObject(stream, this);
        }
    }

Demo fiddle #2 here,它产生了一个相当简单、干净的序列化格式:

{"m_DataBase":[{"Key":1,"Value":[{"Key":2,"Value":[{"Key":3,"Value":{"Value":"hello"}}]}]}]}

如果您愿意使用 3rd 方组件,也可以使用


推荐阅读