当 xml 有缩进/换行符时,字典的自定义序列化失败 [英] Custom serialization of a dictionary fails when xml has indentation/line break

查看:14
本文介绍了当 xml 有缩进/换行符时,字典的自定义序列化失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了让字典序列化的 XML 更清晰,我编写了一个自定义类,它实现了 IXmlSerializable.

In order to have a cleaner XML of a Dictionary serialization, I wrote a custom class, that implements IXmlSerializable.

我的自定义类定义如下:

My custom class is defined like this:

public class MyCollection : System.Collections.Generic.Dictionary<string, string>, IXmlSerializable
{
    private const string XmlElementName = "MyData";
    private const string XmlAttributeId = "Id";

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        reader.MoveToContent();
        while (reader.Read())
        {
            if(reader.LocalName == XmlElementName)
            {
                var tag = reader.GetAttribute(XmlAttributeId);
                var content = reader.ReadElementContentAsString(); 

                this.Add(tag, content);
            }
        }
    }

    public void WriteXml(System.Xml.XmlWriter writer)
    {
        foreach (string key in this.Keys)
        {
            writer.WriteStartElement(XmlElementName);
            writer.WriteAttributeString(XmlAttributeId, key);
            writer.WriteString(this[key]);
            writer.WriteEndElement();
        }
    }
}

我的代码适用于这个 XML 片段:

My code works with this XML snippet:

<MyCollection xmlns="http://schemas.datacontract.org/2004/07/MyProject" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <MyData Id="1">some content</MyData>
    <MyData Id="2">some other content</MyData>
</MyCollection>

然而,当我有这个缩小的 XML 时,我的代码抛出一个异常:

However, when I have this minified XML, my code throws an exception:

<MyCollection xmlns="http://schemas.datacontract.org/2004/07/MyProject" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><MyData Id="1">some content </MyData><MyData Id="2">some other content</MyData></MyCollection>

例外是:

System.InvalidOperationException: The ReadElementContentAsString method is not supported on node type EndElement

它在调用 ReadElementContentAsString 时抛出.

如何修复我的代码?

我可以使用 :

var xml = @"<MyCollection xmlns=""http://schemas.datacontract.org/2004/07/MyProject"" xmlns:i=""http://www.w3.org/2001/XMLSchema-instance""><MyData Id=""1"">some content </MyData><MyData Id=""2"">some other content</MyData></MyCollection>";

var raw = Encoding.UTF8.GetBytes(xml);

var serializer = new DataContractSerializer(typeof(MyCollection));

using (var ms = new MemoryStream(raw))
{
    var result = serializer.ReadObject(ms); // Exception throws here
}

推荐答案

你的问题是 reader.ReadElementContentAsString() 将读取器定位在下一个节点的开头,而不是当前节点的结尾.然后,您随后无条件调用 reader.Read() 消耗下一个节点.当该节点是空白时不会造成任何伤害,但当该节点是一个元素时,该元素被跳过.

Your problem is that reader.ReadElementContentAsString() positions the reader at the beginning of the next node, not the end of the current node. Then, your subsequent unconditional call to reader.Read() consumes that next node. When that node is whitespace no harm is done, but when the node is an element, the element is skipped.

您的 MyCollection 的以下版本解决了这个问题:

The following version of your MyCollection fixes this problem:

public class MyCollection : System.Collections.Generic.Dictionary<string, string>, IXmlSerializable
{
    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        using (var subReader = reader.ReadSubtree())
        {
            XmlKeyValueListHelper.ReadKeyValueXml(subReader, this);
        }
        // Consume the EndElement also (or move past the current element if reader.IsEmptyElement).
        reader.Read();
    }

    public void WriteXml(System.Xml.XmlWriter writer)
    {
        XmlKeyValueListHelper.WriteKeyValueXml(writer, this);
    }
}

public static class XmlKeyValueListHelper
{
    private const string XmlElementName = "MyData";
    private const string XmlAttributeId = "Id";

    public static void WriteKeyValueXml(System.Xml.XmlWriter writer, ICollection<KeyValuePair<string, string>> collection)
    {
        foreach (var pair in collection)
        {
            writer.WriteStartElement(XmlElementName);
            writer.WriteAttributeString(XmlAttributeId, pair.Key);
            writer.WriteString(pair.Value);
            writer.WriteEndElement();
        }
    }

    public static void ReadKeyValueXml(System.Xml.XmlReader reader, ICollection<KeyValuePair<string, string>> collection)
    {
        if (reader.IsEmptyElement)
        {
            reader.Read();
            return;
        }

        reader.ReadStartElement(); // Advance to the first sub element of the list element.
        while (reader.NodeType != XmlNodeType.EndElement)
        {
            if (reader.NodeType == XmlNodeType.Element && reader.LocalName == XmlElementName)
            {
                var tag = reader.GetAttribute(XmlAttributeId);
                string content;
                if (reader.IsEmptyElement)
                {
                    content = string.Empty;
                    // Move past the end of item element
                    reader.Read();
                }
                else
                {
                    // Read content and move past the end of item element
                    content = reader.ReadElementContentAsString();
                }
                collection.Add(new KeyValuePair<string, string>(tag, content));
            }
            else
            {
                // For instance a comment.
                reader.Skip();
            }
        }
        // Move past the end of the list element
        reader.ReadEndElement();
    }
}

一些注意事项:

  • 通过使用 XmlReader.ReadSubtree() 我确保 ReadXml() 不会读取超过 MyCollection 元素的末尾破坏未来的元素——在实现 IXmlSerializable 时很容易犯的一个错误.

  • By using XmlReader.ReadSubtree() I ensure that ReadXml() does not read past the end of the MyCollection element thus corrupting future elements -- an easy mistake to make when implementing IXmlSerializable.

通过检查 reader.NodeType == XmlNodeType.Element &&reader.LocalName == XmlElementName 我忽略了意外类型的节点,例如注释.

By checking for reader.NodeType == XmlNodeType.Element && reader.LocalName == XmlElementName I ignore unexpected types of nodes such as comments.

工作 .Net fiddle.

这篇关于当 xml 有缩进/换行符时,字典的自定义序列化失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆