C#中的IXmlSerializable字典没有'Key'/'Value'节点

时间:2010-08-19 15:40:27

标签: c# dictionary xml-serialization serialization ixmlserializable

我正在尝试在C#中序列化字典。所有the examples我已经能够找到如下所示的创建XML:

<Dictionary>
    <ArrayOfEntries>
        <Entry>
            <Key>myFirstKey</Key>
            <Value>myFirstValue</Value>
        </Entry>
        <Entry>
            <Key>mySecondKey</Key>
            <Value>mySecondValue</Value>
        </Entry>
    </ArrayOfEntries>
</Dictionary>

它有所不同,有时候ArrayOfEntries节点不是必需的,但我仍然看到字典的键值对的常规模式存储在它们自己的节点中。我想要的是以下内容:

<Dictionary>
    <myFirstKey>myFirstValue</myFirstKey>
    <mySecondKey>mySecondValue</mySecondKey>
</Dictionary>

我之前已经编写了ReadXmlWriteXml来执行此操作并且它适用于单个字典,但如果我尝试序列化和反序列化List<T>我的可序列化字典实例,反序列化列表最终只包含 last 可序列化字典。我认为对于我的读写方法必须贪得无厌,以至于它不知道何时停止。以下是我的可序列化字典方法,这些方法目前不适用于List可序列化字典的序列化:

public void WriteXml(XmlWriter writer)
{
    foreach (string key in getKeysToSerialize())
    {
        string cleanKey = key.SanitizeForXml();
        string value = getValueForKey(key).Trim();

        if (isCdata(key, value))
        {
            string cdataFriendlyValue = cleanStringForUseInCdata(value);
            if (string.IsNullOrEmpty(cdataFriendlyValue))
            {
                continue;
            }

            writer.WriteStartElement(cleanKey);
            writer.WriteCData(cdataFriendlyValue);
            writer.WriteEndElement();
        }
        else
        {
            writer.WriteElementString(cleanKey, value);
        }
    }
}

ReadXml

public void ReadXml(XmlReader reader)
{
    string key = null, value = null;
    bool wasEmpty = reader.IsEmptyElement;

    while (reader.Read())
    {
        if (wasEmpty)
        {
            return;
        }

        switch (reader.NodeType)
        {
            case XmlNodeType.Element:
                key = reader.Name;
                if (keyIsSubTree(key))
                {
                    using (XmlReader subReader = reader.ReadSubtree())
                    {
                        storeSubtree(key, subReader);
                    }

                    // Reset key to null so we don't try to parse it as
                    // a regular key-value pair later
                    key = null;
                }
                break;
            case XmlNodeType.Text:
                value = reader.Value;
                break;
            case XmlNodeType.CDATA:
                value = cleanCdataForStoring(reader.Value);
                break;
            case XmlNodeType.EndElement:
                if (!string.IsNullOrEmpty(key))
                {
                    string valueToStore =
                        string.IsNullOrEmpty(value) ? string.Empty : value
                    Add(key, valueToStore);
                }
                key = null;
                value = null;
                break;
        }
    }
}

我在其他教程和我正在做的事情之间注意到的一个区别是许多人使用XmlSerializer来序列化和反序列化ReadXmlWriteXml中的对象,而我使用{ {1}}之类的。

我的问题是,我如何序列化字典,使其XML就像我上面的第二个XML示例,但是WriteElementString的序列化和反序列化也有效?即使只是暗示“为什么你在做什么 blah ,这可能会让它变得有趣”会有所帮助。

1 个答案:

答案 0 :(得分:1)

我的WriteXml似乎没问题,因为它以我想要的XML格式输出字典的内容。 ReadXml是我出错的地方。根据找到的ReadXml实施in this tutorial,我想出了以下内容:

public override void ReadXml(XmlReader reader)
{
    reader.MoveToContent();
    bool isEmptyElement = reader.IsEmptyElement;
    reader.ReadStartElement();
    if (isEmptyElement)
    {
        return;
    }
    while (XmlNodeType.EndElement != reader.NodeType)
    {
        // Bypass XmlNodeType.Whitespace, as found in XML files
        while (XmlNodeType.Element != reader.NodeType)
        {
            if (XmlNodeType.EndElement == reader.NodeType)
            {
                reader.ReadEndElement();
                return;
            }
            reader.Read();
        }
        string key = reader.Name;
        if (keyIsSubTree(key))
        {
            storeSubtree(key, reader.ReadSubtree());
        }
        else
        {
            string value = reader.ReadElementString();
            storeKeyValuePair(key, value ?? string.Empty);
        }
    }
    if (XmlNodeType.EndElement == reader.NodeType)
    {
        reader.ReadEndElement();
    }
}

这似乎适用于只有一个键值对的词典以及具有多个键值对的词典。我对序列化/反序列化List<T>词典的测试也奏效了。我还没有为CDATA添加任何特殊处理,我相信这是必要的。不过,这似乎是一个开始。

编辑:实际上,也许我只需要在时担心CDATA,而不是在阅读时。

修改:更新了ReadXml上面的代码,以便考虑XmlNodeType.Whitespace,这是您在反序列化时在XML文件中找到的。我收到XML错误,因为ReadEndElement不是reader.NodeType时调用了XmlNodeType.EndElement。对其进行检查,以便只有ReadEndElement时调用EndElement,并且还更改外部while循环,以便读者可以进行(通过Read())尚未点击Element且未点击EndElement(例如,它已点击Whitespace)。