C# 中的 IXmlSerialized 字典不带“Key”/“Value”节点

发布于 2024-09-15 11:50:01 字数 3655 浏览 4 评论 0原文

我正在尝试用 C# 序列化字典。所有 示例 我已经找到创建 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 ,反序列化列表最终仅包含最后可序列化字典。我认为我的读取或写入方法一定是贪婪的,以至于它不知道何时停止。以下是我的可序列化字典方法,目前不适用于可序列化字典的 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 中的对象,而我使用 WriteElementString代码>等等。

我的问题是,如何序列化字典,使其 XML 类似于上面的第二个 XML 示例,但序列化和反序列化 List 也可以工作?即使只是暗示“你为什么要这么做,这可能会让事情变得很有趣”也会有所帮助。

I'm trying to serialize a dictionary in C#. All the examples I've been able to find create XML like the following:

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

It varies, sometimes the ArrayOfEntries node isn't necessary, but I still see the regular pattern of the dictionary's key-value pairs being stored in their own nodes. What I would like is something like the following:

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

I have written ReadXml and WriteXml to do this before and it works for a single dictionary, but if I try to serialize and deserialize a List<T> of my serializable dictionary instances, the deserialized list ends up with only the last serializable dictionary in it. I think something must be greedy about my read or write method such that it doesn't know when to stop. Here are my serializable dictionary methods that currently don't work for serialization of List's of serializable dictionaries:

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

And 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;
        }
    }
}

One difference I've noticed between other tutorials and what I'm doing is that many use XmlSerializer to serialize and deserialize objects in their ReadXml and WriteXml, whereas I use WriteElementString and the like.

My question is, how can I serialize a dictionary such that its XML is like my second XML example above, but so that serializing and deserializing a List<MySerializableDictionary> also works? Even just hints of "why are you doing blah, that might make it act funny" would help.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

萧瑟寒风 2024-09-22 11:50:02

我的 WriteXml 看起来没问题,因为它以我想要的 XML 格式输出字典的内容。 ReadXml 是我出错的地方。基于本教程中的 ReadXml 实现,我想出了以下结论:

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 进行序列化/反序列化的测试也有效。我还没有为 CDATA 添加任何特殊处理,我确信这是必要的。不过,这似乎是一个开始。

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

编辑:更新了上面的 ReadXml 代码,以考虑反序列化时在 XML 文件中找到的 XmlNodeType.Whitespace。我收到 XML 错误,因为在 reader.NodeType 不是 XmlNodeType.EndElement 时调用 ReadEndElement。对此进行检查,以便它仅在 EndElement 时调用 ReadEndElement,并且还更改了外部 while 循环,以便读者可以继续进行(通过 Read()),当它没有命中 Element 并且没有命中 EndElement (例如,它命中了 >空白)。

My WriteXml seemed okay because it outputs the dictionary's content in the XML format I want. ReadXml was where I was going wrong. Based on the ReadXml implementation found in this tutorial, I came up with the following:

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

This seemed to work for dictionaries that had only one key-value pair as well as dictionaries with multiple key-value pairs. My test of serializing/deserializing a List<T> of dictionaries also worked. I haven't added any special handling for CDATA yet, and I'm sure that will be necessary. This seems like a start, though.

Edit: actually, maybe I only need to worry about CDATA when I'm writing, not when reading.

Edit: updated ReadXml code above to account for XmlNodeType.Whitespace, which is found in XML files when you're deserializing. I was getting an XML error because ReadEndElement was being called when the reader.NodeType was not an XmlNodeType.EndElement. Put in a check for that so it only calls ReadEndElement when it's an EndElement, and also changed the outer while loop so the reader can progress (via Read()) when it hasn't hit an Element and it hasn't hit an EndElement (e.g., it has hit Whitespace).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文