为什么 XPath 在 System.Xml.XmlDocument 上找不到相邻的文本和 CData 节点?

Why does XPath over a System.Xml.XmlDocument not find adjacent text and CData nodes?

为什么 System.Xml.XmlDocument 上的 XPath 找不到相邻的文本和 CData 节点?

var raw_xml = @"
<root>
    <test>
        <![CDATA[This is a CDATA node]]>And this is an adjacent text node
    </test>
</root>
";

var doc = new XmlDocument();
doc.LoadXml(raw_xml);

var results = doc.SelectNodes("/root/test/text()");
Console.WriteLine(results.Count); // gives: 1
Console.WriteLine(results[0].Value); // gives: This is a CDATA node
Console.WriteLine(results[0].Name); // gives: #cdata-section
Console.WriteLine(results[0].GetType().FullName); // gives: System.Xml.XmlCDataSection
Console.WriteLine(results[0].NextSibling.Name); // gives: #text
Console.WriteLine(results[0].NextSibling.Value.Trim()); // gives: And this is an adjacent text node

从上面我们可以看出,CDATA 节点有文本节点,因为它是下一个兄弟节点,所以您会认为表达式 /root/test/text() 会找到它。

与 XPath 相同的结果:/root/test/node()

在处理 XML 文档时,您可能已经习惯了文档对象模型 (DOM),其中 CDATA 节点与文本节点是分开的。 XPath 数据模型将 text() 节点视为所有相邻的 CDATA 和文本 DOM 节点兄弟姐妹。

因此,尝试编写一个将特定 DOM text/CDATA 节点不是相邻系列的第一个节点的查询将失败,例如:

var results = doc.SelectNodes("/root/test/text()[starts-with(., 'And')]");
Console.WriteLine(results.Count); // gives: 0

实际上,尝试通过其他 XPath select "text" DOM 节点意味着:

var results = doc.SelectNodes("/root/test/text()[contains(., 'text node')]");

将给出与 OP 中初始 /root/test/text() 查询相同的结果。

您看到的是两种模型的混合——XPath 查询的结果被转换回 DOM 节点;所以它给你第一个 text() 节点,在本例中是 CDATA 节点。

如果您确实需要在 XPath 中使用单独的文本和 CDATA 节点,您将需要确保 XML 注释分隔源文档中的节点,如下所示:

<root>
    <test>
        <![CDATA[This is a CDATA node]]><!-- separator comment -->And this is an adjacent text node
    </test>
</root>

所以

var results = doc.SelectNodes("/root/test/text()");
Console.WriteLine(results.Count);

会给2.