使用 XPath 导航选择节点
本文内容
XML 文档对象模型 (DOM) 包含的方法使您可以使用 XML 路径语言 (XPath) 浏览功能查询 DOM 中的信息。 可以使用 XPath 查找单个特定节点,或查找与某个条件匹配的所有节点。
XPath 选择方法
DOM 类提供两种 XPath 选择方法:SelectSingleNode 方法和 SelectNodes 方法。 SelectSingleNode 方法返回符合选择条件的第一个节点。 SelectNodes 方法返回包含匹配节点的 XmlNodeList。
下面的示例使用 SelectSingleNode 方法来选择其作者姓氏符合指定条件的第一个 book
节点。 bookstore.xml 文件(在本主题末尾提供)用作输入文件。
// Load the document and set the root element.
XmlDocument doc = new XmlDocument();
doc.Load("bookstore.xml");
XmlNode root = doc.DocumentElement;
// Add the namespace.
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("bk", "urn:newbooks-schema");
// Select and display the first node in which the author's
// last name is Kingsolver.
XmlNode node = root.SelectSingleNode(
"descendant::bk:book[bk:author/bk:last-name='Kingsolver']", nsmgr);
Console.WriteLine(node.InnerXml);
下一个示例使用 SelectNodes 方法来选择其价格大于指定金额的所有书节点。 然后,以编程方式将选定列表中的每本书的价格减去 10%。 最后,将更新的文件写入控制台。 bookstore.xml 文件(在本主题末尾提供)用作输入文件。
// Load the document and set the root element.
XmlDocument doc = new XmlDocument();
doc.Load("bookstore.xml");
XmlNode root = doc.DocumentElement;
// Add the namespace.
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("bk", "urn:newbooks-schema");
// Select all nodes where the book price is greater than 10.00.
XmlNodeList nodeList = root.SelectNodes(
"descendant::bk:book[bk:price>10.00]", nsmgr);
foreach (XmlNode book in nodeList)
{
// Discount prices by 10%.
double price;
price = Math.Round(Convert.ToSingle(
book.LastChild.InnerText) * 0.9, 2);
book.LastChild.InnerText = price.ToString();
}
// Display the updated document.
doc.Save(Console.Out);
上面的示例从文档元素开始执行 XPath 查询。 设置 XPath 查询的起始点即设置了上下文节点,该节点是 XPath 查询的起始点。 如果不希望在文档元素处开始,而是希望从文档元素的第一个子级开始,可以编写 Select 语句的代码,如下所示:
this doc.DocumentElement.FirstChild.SelectNodes(. . .);
所有 XmlNodeList 对象都与基础文档同步。 因此,如果循环访问节点列表并修改某个节点的值,则该节点在包含它的文档中也被更新。 请注意,在上一个示例中,在选定的 XmlNodeList 中修改节点时,也会修改基础文档。
备注
修改基础文档时,最好重新运行此 Select 语句。 如果修改的节点可能导致该节点被添加到节点列表(当先前没有添加它时),或者现在会导致它从节点列表中被移除,则无法保证节点列表现在是精确的。
XPath 表达式中的命名空间
XPath 表达式可以包含命名空间。 使用 XmlNamespaceManager 支持命名空间解析。 如果 XPath 表达式包含前缀,前缀和命名空间 URI 对必须添加到 XmlNamespaceManager,并且 XmlNamespaceManager 传递给 SelectNodes(String, XmlNamespaceManager) 或 SelectSingleNode(String, XmlNamespaceManager) 方法。 请注意,上面的代码示例使用 XmlNamespaceManager 来解析 bookstore.xml 文档的命名空间。
备注
如果 XPath 表达式不包含前缀,则假定命名空间统一资源标识符 (URI) 是空的命名空间。 如果 XML 包含默认命名空间,仍必须将前缀和命名空间 URI 添加到 XmlNamespaceManager;否则,不会选择任何节点。
输入文件
下面的 bookstore.xml 文件在本主题的示例中用作输入文件。
<?xml version='1.0'?>
<bookstore xmlns="urn:newbooks-schema">
<book genre="novel" style="hardcover">
<title>The Handmaid's Tale</title>
<author>
<first-name>Margaret</first-name>
<last-name>Atwood</last-name>
</author>
<price>19.95</price>
</book>
<book genre="novel" style="other">
<title>The Poisonwood Bible</title>
<author>
<first-name>Barbara</first-name>
<last-name>Kingsolver</last-name>
</author>
<price>11.99</price>
</book>
<book genre="novel" style="paperback">
<title>The Bean Trees</title>
<author>
<first-name>Barbara</first-name>
<last-name>Kingsolver</last-name>
</author>
<price>5.99</price>
</book>
</bookstore>
请参阅
XPath 查询和命名空间
本文内容
XPath 查询支持 XML 文档中的命名空间,可以使用命名空间前缀来限定元素和属性的名称。 使用命名空间前缀来限定元素和属性的名称可以限制 XPath 查询只返回属于特定命名空间的节点。
例如,如果前缀 books
映射到命名空间 http://www.contoso.com/books
,以下 XPath 查询 /books:books/books:book
将只选择命名空间 book
中的 http://www.contoso.com/books
元素。
XmlNamespaceManager
要在 XPath 查询中使用命名空间,构造一个从 IXmlNamespaceResolver 接口派生的对象(例如 XmlNamespaceManager 类),包含要加入 XPath 查询的命名空间 URI 和前缀。
XmlNamespaceManager 对象可以通过下列任意方式在查询中使用。
-
使用 XmlNamespaceManager 对象的 XPathExpression 方法将 SetContext 对象与现有的 XPathExpression 对象关联。 还可以使用静态 XPathExpression 方法编译新的 Compile 对象,该方法使用表示 XPath 表达式的字符串和 XmlNamespaceManager 对象作为参数,并返回一个新的 XPathExpression 对象。
-
XmlNamespaceManager 对象本身与表示 XPath 表达式的字符串一起作为参数传递给接受方的 XPathNavigator 类方法。
以下是 XPathNavigator 类中接受从 IXmlNamespaceResolver 接口派生的对象作为参数的方法。
默认命名空间
在下面的 XML 文档中,具有空前缀的默认命名空间用于声明 http://www.contoso.com/books
命名空间。
<books xmlns="http://www.contoso.com/books">
<book>
<title>Title</title>
<author>Author Name</author>
<price>5.50</price>
</book>
</books>
XPath 将空前缀作为 null
命名空间对待。 也就是说,XPath 查询中只能使用映射到命名空间上的前缀。 这意味着如果要针对 XML 文档中的某个命名空间进行查询,即使是默认的命名空间,也需要为其定义前缀。
例如,在没有为上面的 XML 文档定义前缀的情况下,XPath 查询 /books/book
不会返回任何结果。
必须绑定前缀,这样,如果查询的文档中有些节点在某个命名空间,有些节点在默认命名空间,可以避免混淆情况。
以下代码为默认命名空间定义前缀,并选择 book
命名空间中的所有 http://www.contoso.com/books
元素。
XPathDocument document = new XPathDocument("books.xml");
XPathNavigator navigator = document.CreateNavigator();
XPathExpression query = navigator.Compile("/books:books/books:book");
XmlNamespaceManager manager = new XmlNamespaceManager(navigator.NameTable);
manager.AddNamespace("books", "http://www.contoso.com/books");
query.SetContext(manager);
XPathNodeIterator nodes = navigator.Select(query);
请参阅
XPath 命名空间浏览
本文内容
要对 XML 文档使用 XPath 查询,必须正确定位 XML 命名空间以及命名空间中包含的元素。 命名空间可防止在多个上下文中使用名称时可能产生的混淆情况;例如,名称 ID
可能引用与 XML 文档的不同元素相关联的多个标识符。 命名空间语法指定了 URI、名称和前缀,可区分 XML 文档的各个元素。
本主题中的示例演示了通过 XPathNavigator 在浏览 XML 文档时使用前缀。 有关命名空间和语法的详细信息,请参阅 XML 文件:了解 XML 命名空间。
命名空间声明
命名空间声明使得在使用 XPathNavigator 的实例时,很容易区分和定位 XML 文档的各个元素。 命名空间前缀提供了一种简化的语法,用来定位命名空间。
前缀由以下形式定义:<e:Envelope xmlns:e=http://schemas.xmlsoap.org/soap/envelope/>.
在此语法中,前缀“e
”是命名空间的正式 URI 的缩写。 使用此语法可以将 Body
元素标识为 Envelope
命名空间的成员:e:Body
。
在下一节的浏览示例中,下面的 XML 文档将用作 response.xml
。
<?xml version="1.0" encoding="utf-8" ?>
<e:Envelope xmlns:e="http://schemas.xmlsoap.org/soap/envelope/">
<e:Body>
<s:Search xmlns:s="http://schemas.microsoft.com/v1/Search">
<r:request xmlns:r="http://schemas.microsoft.com/v1/Search/metadata"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
</r:request>
</s:Search>
</e:Body>
</e:Envelope>
通过命名空间前缀进行浏览
本节中的代码使用 XPathNavigator 和 XmlNamespaceManager 对象,从前一节的 XML 文档中选择 Search
元素。 查询 xpath
对路径中的每个元素都包含了命名空间前缀。 指定包含每个元素的命名空间的精确标识,可确保通过 Search
方法正确浏览至 SelectSingleNode 元素。
using (XmlReader reader = XmlReader.Create("response.xml"))
{
XPathDocument doc = new XPathDocument(reader);
XPathNavigator nav = doc.CreateNavigator();
XmlNamespaceManager nsmgr = new XmlNamespaceManager(nav.NameTable);
nsmgr.AddNamespace("e", @"http://schemas.xmlsoap.org/soap/envelope/");
nsmgr.AddNamespace("s", @"http://schemas.microsoft.com/v1/Search");
nsmgr.AddNamespace("r", @"http://schemas.microsoft.com/v1/Search/metadata");
nsmgr.AddNamespace("i", @"http://www.w3.org/2001/XMLSchema-instance");
string xpath = "/e:Envelope/e:Body/s:Search";
XPathNavigator element = nav.SelectSingleNode(xpath, nsmgr);
Console.WriteLine("Element Prefix:" + element.Prefix +
" Local name:" + element.LocalName);
Console.WriteLine("Namespace URI: " + element.NamespaceURI);
}
完全限定的命名空间和名称的精确度不仅仅是一种方便。 对前面的示例中的文档定义和代码进行一项小实验,可以验证如果不使用完全限定的元素名称进行浏览,就会引发异常。 例如,元素定义为 <Search xmlns="http://schemas.microsoft.com/v1/Search">
,而查询字符串 xpath = "/s:Envelope/s:Body/Search";
没有对 Search
元素使用命名空间前缀,则此查询将返回 null
,而不是 Search
元素。