1

我有一堆与空格处理有关的问题XmlDocument。请参阅下面示例中的编号注释。

  1. 在混合模式下,所有空格不应该很重要吗?为什么标签之间的空间a不显着?

  2. 虽然我知道实际的空白元素仍然是XmlWhitespace,但如何将这些空间标准化为XmlSignificantWhitespace节点?Normalize()不起作用。

  3. 我唯一的选择是手动吗?

这是我的测试用例:

private static void Main()
{
    // 1. Shouldn't all whitespace be significant in mixed mode? Why the space between the a tags is not significant?
    var doc = new XmlDocument
    {
        InnerXml = "<root>test1 <a>test2</a> <a>test3</a></root>",
    };
    PrintDoc(doc);

    // 2.a. While I understand that the actual whitespace element is still XmlWhitespace, how do I normalize these spaces into XmlSignificantWhitespaces?
    doc.DocumentElement.RemoveAll();
    doc.DocumentElement.SetAttribute("xml:space", "preserve");
    var fragment = doc.CreateDocumentFragment();
    fragment.InnerXml = "test1 <a>test2</a> <a>test3</a>";
    doc.DocumentElement.PrependChild(fragment);
    PrintDoc(doc);

    // 2.b. Normalize doesn't work
    doc.Normalize();
    PrintDoc(doc);

    // 3.a. Manual normalization does work, is there a better way?
    doc.DocumentElement.RemoveAllAttributes();
    var whitespaces = doc.DocumentElement.ChildNodes.Cast<XmlNode>()
        .OfType<XmlWhitespace>()
        .ToList();
    foreach (var whitespace in whitespaces)
    {
        var significant = doc.CreateSignificantWhitespace(whitespace.Value);
        doc.DocumentElement.ReplaceChild(significant, whitespace);
    }
    PrintDoc(doc);

    // 3.b. Reading from string also works
    doc.InnerXml = "<root xml:space=\"preserve\">test1 <a>test2</a> <a>test3</a></root>";
    PrintDoc(doc);
}

private static void PrintDoc(XmlDocument doc)
{
    var nodes = doc.DocumentElement.ChildNodes.Cast<XmlNode>().ToList();
    var whitespace = nodes.OfType<XmlWhitespace>().Count();
    var significantWhitespace = nodes.OfType<XmlSignificantWhitespace>().Count();

    Console.WriteLine($"Xml: {doc.InnerXml}\nwhitespace: {whitespace}\nsignificant whitespace: {significantWhitespace}\n");
}

输出如下:

Xml: <root>test1 <a>test2</a><a>test3</a></root>
whitespace: 0
significant whitespace: 0

Xml: <root xml:space="preserve">test1 <a>test2</a> <a>test3</a></root>
whitespace: 1
significant whitespace: 0

Xml: <root xml:space="preserve">test1 <a>test2</a> <a>test3</a></root>
whitespace: 1
significant whitespace: 0

Xml: <root>test1 <a>test2</a> <a>test3</a></root>
whitespace: 0
significant whitespace: 1

Xml: <root xml:space="preserve">test1 <a>test2</a> <a>test3</a></root>
whitespace: 0
significant whitespace: 1
4

2 回答 2

3

Microsoft 文档不清楚,至少部分不准确。尽管XmlSignificantWhitespace 类的 Microsoft 文档说“混合内容节点中标记之间的空白” “重要空白”,但实际的 XmlDocument 加载和解析行为与此不一致。相关文档是PreserveWhitespaceWhite Space 以及加载 DOM 时的重要空白处理,但这些没有提供足够的具体细节。

根据经验,正如您在测试用例和我自己的测试中所展示的那样,行为如下:

  • XmlDocument.PreserveWhitespace = true加载时并在范围内保留空白xml:space="preserve"。但是,对于前者,它保存在Whitespace节点中而不是SignificantWhitespace节点中。
  • 如果XmlDocument.PreserveWhitespace = false,则混合内容节点中元素之间的空白将被丢弃,这与XmlSignificantWhitespace 类文档相反。
  • 空白变成范围SignfiicantWhitespace内的节点xml:space="preserve"。在这种情况下,无论设置如何,它始终保留为。SignificantWhitespaceXmlDocument.PreserveWhitespace

简而言之,将空白直接解析为SignificantWhitespace节点的唯一方法是在xml:space="preserve"范围内。一种可能对您有用的方法是将您的 XML 内容包装在具有xml:space="preserve"范围的新外部元素中。我不知道为什么您的CreateDocumentFragment()测试不起作用,但这里有一些确实有效的代码:

// 4. Loading the XML within an xml:space="preserve" element works
doc.InnerXml = "<root xml:space=\"preserve\"></root>";
doc.FirstChild.InnerXml = "test1 <a>test2</a> <a>test3</a>";
PrintDoc(doc);

这导致:

Xml: <root xml:space="preserve">test1 <a>test2</a> <a>test3</a></root>
whitespace: 0
significant whitespace: 1
于 2016-01-19T20:57:45.797 回答
1

自己编写XmlNodeReader似乎可行,尽管它不是“最干净”的解决方案。

考虑这里的当前实现:

public virtual XmlNodeType MoveToContent() {
    do {
        switch (this.NodeType) {
            case XmlNodeType.Attribute:
                MoveToElement();
                goto case XmlNodeType.Element;
            case XmlNodeType.Element:
            case XmlNodeType.EndElement:
            case XmlNodeType.CDATA:
            case XmlNodeType.Text:
            case XmlNodeType.EntityReference:
            case XmlNodeType.EndEntity:
                return this.NodeType;
        }
    } while (Read());
    return this.NodeType;
}

要获得标记SignificantWhitespace为内容,您可以返回NodeTypewhen it is XmlNodeType.SignificantWhitespace

这是我自己的完整实现WhitespaceXmlNodeReader

internal class WhitespaceXmlNodeReader : XmlNodeReader
{
    public WhitespaceXmlNodeReader(XmlNode node)
        : base(node)
    {
    }

    public override XmlNodeType MoveToContent()
    {
        do
        {
            switch (NodeType)
            {
                case XmlNodeType.Attribute:
                    MoveToElement();
                    goto case XmlNodeType.Element;
                case XmlNodeType.Element:
                case XmlNodeType.EndElement:
                case XmlNodeType.CDATA:
                case XmlNodeType.Text:
                case XmlNodeType.EntityReference:
                case XmlNodeType.EndEntity:
                // This was added:
                case XmlNodeType.SignificantWhitespace:
                    return NodeType;
            }
        } while (Read());
        return NodeType;
    }
}
于 2016-01-20T08:07:42.300 回答