当xml有缩进/换行符时,字典的自定义序列化失败

史蒂夫·B

为了使Dictionary序列化的XML更加整洁,我编写了一个实现的自定义类IXmlSerializable

我的自定义类的定义如下:

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代码段一起工作:

<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时,我的代码将引发异常:

<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()消耗该下一个节点。当该节点为空白时,不会造成任何危害,但是当该节点为元素时,将跳过该元素。

您的以下版本MyCollection可解决此问题:

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

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

工作。净提琴

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

在PyYAML中使用自定义类密钥对字典进行反序列化失败

如何使用 DataContractSerializer 自定义字典序列化?

GSON 序列化因自定义对象而失败

类的自定义序列化失败

JSON-LD序列化字符串,在RDF4J / Sesame中没有缩进,空格或换行符

XML反序列化C#,换行符\ n替换为\\ n

在序列化多维数组时,如何防止JsonWriter添加太多换行符?

序列化消息中的换行符

带有自定义属性的 Ruby 类实例的 XML 序列化

具有自定义表单的序列化代理?

默认序列化和自定义序列化有什么区别?

自定义身份验证反序列化失败:解析值时遇到意外字符

Java自定义序列化

自定义序列化

自定义反序列化

序列化自定义配置

按自定义换行符聚合

如何使用Json.Net使用自定义键对字典进行序列化/反序列化?

Stream 测试用例上的自定义序列化和反序列化失败

在 Laravel 中插入/更新模型时,有没有办法自定义 JSON 序列化?

使用XamlServices.Save()序列化XAML文件时如何在每个属性后添加换行符

带有位大小定义的 Java 自定义序列化和反序列化

Django Rest Framework将查询集序列化为带有自定义字段的字典

使用jackson-dataformat-xml自定义根元素的XML反序列化?

自定义Xml序列化器以将值作为xml元素

在 Jackson 中使用自定义序列化程序序列化嵌套对象时出现 JsonGenerationException

JSON Jackson-使用自定义序列化程序序列化多态类时的异常

ServiceStack使用制表符和换行符反序列化json

EF Core中具有自动序列化/反序列化功能的自定义类型