活动公告

系统通知
06-22 18:10
系统通知
06-14 00:00
系统通知
通知:本站资源由网友上传分享,如有违规等问题请到版务模块进行投诉,资源失效请在帖子内回复要求补档,会尽快处理!
10-23 09:31

XPath XML命名空间处理完全指南从基础到高级掌握实用技巧解决XML查询中的命名空间问题提升开发效率

SunJu_FaceMall

3万

主题

3079

科技点

3万

积分

执行版主

碾压王

积分
32876

塔罗立华奏

执行版主 发表于 2025-9-21 23:50:03 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
引言

XML(可扩展标记语言)作为一种广泛使用的数据交换格式,在众多应用场景中扮演着重要角色。而XPath则是XML文档查询和导航的强大工具,它提供了一种简洁的方式来定位XML文档中的元素和属性。然而,当XML文档使用命名空间(Namespaces)时,XPath查询变得复杂起来,许多开发者在这一环节遇到困难。

XML命名空间的设计初衷是为了避免元素和属性名称的冲突,但它确实给XPath查询带来了额外的复杂性。本指南将从基础概念出发,逐步深入到高级技巧,全面介绍如何在XPath中有效处理XML命名空间问题,帮助开发者提升处理XML文档的效率和能力。

XML命名空间基础

什么是XML命名空间?

XML命名空间是一种避免元素和属性命名冲突的机制。在XML文档中,不同的词汇表(vocabularies)可能会使用相同的元素名称,命名空间通过将这些名称与唯一的URI(统一资源标识符)关联起来,确保了名称的唯一性。

命名空间的声明语法

XML命名空间通过特殊的属性声明,通常以”xmlns:“开头:
  1. <root xmlns:book="http://www.example.com/books">
  2.     <book:title>XML Guide</book:title>
  3. </root>
复制代码

在这个例子中:

• xmlns:book声明了一个前缀为”book”的命名空间
• http://www.example.com/books是命名空间的URI
• book:title使用了这个命名空间

默认命名空间

XML还支持默认命名空间,即不使用前缀的命名空间:
  1. <root xmlns="http://www.example.com/default">
  2.     <title>Default Namespace</title>
  3. </root>
复制代码

在这个例子中,<root>元素及其所有没有前缀的子元素都属于默认命名空间http://www.example.com/default。

命名空间的作用域

命名空间声明的作用域从声明元素开始,到其对应的结束元素为止。子元素可以继承父元素的命名空间声明,也可以覆盖或声明新的命名空间。
  1. <root xmlns:book="http://www.example.com/books">
  2.     <book:library>
  3.         <book:book xmlns:book="http://www.example.com/new-books">
  4.             <book:title>New Book</book:title>
  5.         </book:book>
  6.     </book:library>
  7. </root>
复制代码

在这个例子中,内部的<book:book>元素重新定义了book前缀,覆盖了外部定义。

XPath基础

XPath表达式的基本语法

XPath使用路径表达式来选取XML文档中的节点或节点集。这些路径表达式类似于文件系统中的路径。
  1. <!-- 示例XML文档 -->
  2. <bookstore>
  3.     <book category="cooking">
  4.         <title lang="en">Everyday Italian</title>
  5.         <author>Giada De Laurentiis</author>
  6.         <year>2005</year>
  7.         <price>30.00</price>
  8.     </book>
  9.     <book category="children">
  10.         <title lang="en">Harry Potter</title>
  11.         <author>J.K. Rowling</author>
  12.         <year>2005</year>
  13.         <price>29.99</price>
  14.     </book>
  15. </bookstore>
复制代码

基本的XPath表达式示例:
  1. /bookstore/book/title  <!-- 选取所有book元素下的title子元素 -->
  2. //title               <!-- 选取文档中所有的title元素 -->
  3. //@category           <!-- 选取所有名为category的属性 -->
  4. /bookstore/book[1]    <!-- 选取bookstore下的第一个book元素 -->
  5. //title[@lang='en']   <!-- 选取所有具有lang属性且值为en的title元素 -->
复制代码

轴(Axes)

XPath轴定义了相对于当前节点的节点集。常用的轴包括:

• child:选取当前节点的所有子元素(默认轴)
• attribute:选取当前节点的所有属性
• descendant:选取当前节点的所有后代元素(子、孙等)
• ancestor:选取当前节点的所有祖先元素(父、祖父等)
• following-sibling:选取当前节点之后的所有同级节点
• preceding-sibling:选取当前节点之前的所有同级节点
  1. child::book      <!-- 选取当前节点的所有book子元素 -->
  2. attribute::lang  <!-- 选取当前节点的lang属性 -->
  3. descendant::title <!-- 选取当前节点的所有title后代元素 -->
复制代码

节点测试

节点测试用于筛选轴中的节点。常见的节点测试包括:

• 节点名称(如book、title)
• node():任何类型的节点
• text():文本节点
• comment():注释节点
• processing-instruction():处理指令
  1. child::text()    <!-- 选取当前节点的所有文本子节点 -->
  2. child::node()    <!-- 选取当前节点的所有类型的子节点 -->
复制代码

谓语(Predicates)

谓语用于查找某个特定的节点或者包含某个指定值的节点,被嵌在方括号[]中。
  1. /bookstore/book[1]          <!-- 选取bookstore下的第一个book元素 -->
  2. //book[price>35.00]         <!-- 选取所有price元素值大于35的book元素 -->
  3. //book[category='cooking']  <!-- 选取所有category属性为cooking的book元素 -->
  4. //book[position()<3]        <!-- 选取前两个book元素 -->
复制代码

函数和运算符

XPath提供了丰富的函数库和运算符,用于处理和筛选数据。

常用函数:

• count(node-set):计算节点集中的节点数
• string(value):将值转换为字符串
• concat(string, string, ...):连接字符串
• starts-with(string, string):检查字符串是否以指定字符串开头
• contains(string, string):检查字符串是否包含指定字符串
• substring(string, start, length):提取子字符串
• number(value):将值转换为数字
• sum(node-set):计算节点集中所有数值节点的和
  1. count(//book)                          <!-- 计算文档中book元素的数量 -->
  2. string(//book[1]/price)                <!-- 将第一个book的price转换为字符串 -->
  3. concat(//book[1]/title, ' by ', //book[1]/author) <!-- 连接字符串 -->
  4. //book[starts-with(title, 'Everyday')] <!-- 选取title以Everyday开头的book -->
  5. //book[contains(title, 'Potter')]      <!-- 选取title包含Potter的book -->
  6. sum(//book/price)                      <!-- 计算所有book的price总和 -->
复制代码

XPath中的命名空间问题

当XML文档使用命名空间时,简单的XPath表达式往往无法正确匹配元素,这是开发者经常遇到的问题。让我们通过一个例子来说明这个问题:
  1. <!-- 带有命名空间的XML文档 -->
  2. <bookstore xmlns:book="http://www.example.com/books" xmlns:auth="http://www.example.com/authors">
  3.     <book:book category="cooking">
  4.         <book:title lang="en">Everyday Italian</book:title>
  5.         <auth:author>Giada De Laurentiis</auth:author>
  6.         <book:year>2005</book:year>
  7.         <book:price>30.00</book:price>
  8.     </book:book>
  9.     <book:book category="children">
  10.         <book:title lang="en">Harry Potter</book:title>
  11.         <auth:author>J.K. Rowling</auth:author>
  12.         <book:year>2005</book:year>
  13.         <book:price>29.99</book:price>
  14.     </book:book>
  15. </bookstore>
复制代码

为什么简单的XPath表达式无法匹配带有命名空间的元素?

对于上面的XML文档,如果我们尝试使用简单的XPath表达式:
  1. //book
复制代码

这个表达式将无法匹配任何元素,因为XML文档中的book元素实际上属于http://www.example.com/books命名空间,而XPath表达式中的book没有指定命名空间。

默认命名空间的影响

默认命名空间也会带来问题。考虑以下XML文档:
  1. <bookstore xmlns="http://www.example.com/bookstore">
  2.     <book category="cooking">
  3.         <title lang="en">Everyday Italian</title>
  4.         <author>Giada De Laurentiis</author>
  5.         <year>2005</year>
  6.         <price>30.00</price>
  7.     </book>
  8.     <book category="children">
  9.         <title lang="en">Harry Potter</title>
  10.         <author>J.K. Rowling</author>
  11.         <year>2005</year>
  12.         <price>29.99</price>
  13.     </book>
  14. </bookstore>
复制代码

在这个例子中,所有元素都属于默认命名空间http://www.example.com/bookstore。如果我们尝试使用XPath表达式:
  1. //book
复制代码

同样无法匹配任何元素,因为XPath表达式中的book没有命名空间,而XML文档中的book元素属于默认命名空间。

命名空间冲突问题

当不同的命名空间使用相同的元素名称时,可能会导致命名空间冲突:
  1. <document xmlns:doc="http://www.example.com/document" xmlns:meta="http://www.example.com/metadata">
  2.     <doc:title>Document Title</doc:title>
  3.     <meta:title>Metadata Title</meta:title>
  4. </document>
复制代码

在这个例子中,有两个不同的title元素,分别属于不同的命名空间。如果我们想要选择特定的title元素,必须指定正确的命名空间。

解决命名空间问题的基本方法

使用命名空间前缀

最直接的方法是在XPath表达式中使用命名空间前缀。但是,这需要我们在执行XPath查询之前,将命名空间URI与前缀关联起来。
  1. import javax.xml.parsers.DocumentBuilder;
  2. import javax.xml.parsers.DocumentBuilderFactory;
  3. import javax.xml.xpath.XPath;
  4. import javax.xml.xpath.XPathConstants;
  5. import javax.xml.xpath.XPathExpression;
  6. import javax.xml.xpath.XPathFactory;
  7. import javax.xml.namespace.NamespaceContext;
  8. import org.w3c.dom.Document;
  9. import org.w3c.dom.NodeList;
  10. import java.io.StringReader;
  11. import org.xml.sax.InputSource;
  12. public class XPathNamespaceExample {
  13.     public static void main(String[] args) throws Exception {
  14.         String xml = "<bookstore xmlns:book="http://www.example.com/books" xmlns:auth="http://www.example.com/authors">" +
  15.                      "    <book:book category="cooking">" +
  16.                      "        <book:title lang="en">Everyday Italian</book:title>" +
  17.                      "        <auth:author>Giada De Laurentiis</auth:author>" +
  18.                      "        <book:year>2005</book:year>" +
  19.                      "        <book:price>30.00</book:price>" +
  20.                      "    </book:book>" +
  21.                      "    <book:book category="children">" +
  22.                      "        <book:title lang="en">Harry Potter</book:title>" +
  23.                      "        <auth:author>J.K. Rowling</auth:author>" +
  24.                      "        <book:year>2005</book:year>" +
  25.                      "        <book:price>29.99</book:price>" +
  26.                      "    </book:book>" +
  27.                      "</bookstore>";
  28.         // 创建DOM文档
  29.         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  30.         factory.setNamespaceAware(true); // 重要:启用命名空间支持
  31.         DocumentBuilder builder = factory.newDocumentBuilder();
  32.         Document doc = builder.parse(new InputSource(new StringReader(xml)));
  33.         // 创建XPath对象
  34.         XPathFactory xPathFactory = XPathFactory.newInstance();
  35.         XPath xpath = xPathFactory.newXPath();
  36.         // 设置命名空间上下文
  37.         NamespaceContext ctx = new SimpleNamespaceContext();
  38.         xpath.setNamespaceContext(ctx);
  39.         // 使用命名空间前缀的XPath表达式
  40.         XPathExpression expr = xpath.compile("//book:book");
  41.         NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
  42.         // 输出结果
  43.         for (int i = 0; i < nodes.getLength(); i++) {
  44.             System.out.println(nodes.item(i).getTextContent());
  45.         }
  46.     }
  47. }
  48. // 简单的命名空间上下文实现
  49. class SimpleNamespaceContext implements NamespaceContext {
  50.     @Override
  51.     public String getNamespaceURI(String prefix) {
  52.         if ("book".equals(prefix)) {
  53.             return "http://www.example.com/books";
  54.         } else if ("auth".equals(prefix)) {
  55.             return "http://www.example.com/authors";
  56.         }
  57.         return null;
  58.     }
  59.     @Override
  60.     public String getPrefix(String namespaceURI) {
  61.         if ("http://www.example.com/books".equals(namespaceURI)) {
  62.             return "book";
  63.         } else if ("http://www.example.com/authors".equals(namespaceURI)) {
  64.             return "auth";
  65.         }
  66.         return null;
  67.     }
  68.     @Override
  69.     public java.util.Iterator<String> getPrefixes(String namespaceURI) {
  70.         java.util.Set<String> prefixes = new java.util.HashSet<String>();
  71.         if ("http://www.example.com/books".equals(namespaceURI)) {
  72.             prefixes.add("book");
  73.         } else if ("http://www.example.com/authors".equals(namespaceURI)) {
  74.             prefixes.add("auth");
  75.         }
  76.         return prefixes.iterator();
  77.     }
  78. }
复制代码
  1. from lxml import etree
  2. xml = """
  3. <bookstore xmlns:book="http://www.example.com/books" xmlns:auth="http://www.example.com/authors">
  4.     <book:book category="cooking">
  5.         <book:title lang="en">Everyday Italian</book:title>
  6.         <auth:author>Giada De Laurentiis</auth:author>
  7.         <book:year>2005</book:year>
  8.         <book:price>30.00</book:price>
  9.     </book:book>
  10.     <book:book category="children">
  11.         <book:title lang="en">Harry Potter</book:title>
  12.         <auth:author>J.K. Rowling</auth:author>
  13.         <book:year>2005</book:year>
  14.         <book:price>29.99</book:price>
  15.     </book:book>
  16. </bookstore>
  17. """
  18. # 解析XML
  19. doc = etree.fromstring(xml)
  20. # 定义命名空间映射
  21. ns = {
  22.     'book': 'http://www.example.com/books',
  23.     'auth': 'http://www.example.com/authors'
  24. }
  25. # 使用命名空间前缀的XPath表达式
  26. books = doc.xpath('//book:book', namespaces=ns)
  27. # 输出结果
  28. for book in books:
  29.     print(etree.tostring(book, encoding='unicode'))
复制代码

处理默认命名空间

处理默认命名空间时,我们需要为其分配一个前缀,然后在XPath表达式中使用这个前缀。
  1. import javax.xml.parsers.DocumentBuilder;
  2. import javax.xml.parsers.DocumentBuilderFactory;
  3. import javax.xml.xpath.XPath;
  4. import javax.xml.xpath.XPathConstants;
  5. import javax.xml.xpath.XPathExpression;
  6. import javax.xml.xpath.XPathFactory;
  7. import javax.xml.namespace.NamespaceContext;
  8. import org.w3c.dom.Document;
  9. import org.w3c.dom.NodeList;
  10. import java.io.StringReader;
  11. import org.xml.sax.InputSource;
  12. public class XPathDefaultNamespaceExample {
  13.     public static void main(String[] args) throws Exception {
  14.         String xml = "<bookstore xmlns="http://www.example.com/bookstore">" +
  15.                      "    <book category="cooking">" +
  16.                      "        <title lang="en">Everyday Italian</title>" +
  17.                      "        <author>Giada De Laurentiis</author>" +
  18.                      "        <year>2005</year>" +
  19.                      "        <price>30.00</price>" +
  20.                      "    </book>" +
  21.                      "    <book category="children">" +
  22.                      "        <title lang="en">Harry Potter</title>" +
  23.                      "        <author>J.K. Rowling</author>" +
  24.                      "        <year>2005</year>" +
  25.                      "        <price>29.99</price>" +
  26.                      "    </book>" +
  27.                      "</bookstore>";
  28.         // 创建DOM文档
  29.         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  30.         factory.setNamespaceAware(true); // 重要:启用命名空间支持
  31.         DocumentBuilder builder = factory.newDocumentBuilder();
  32.         Document doc = builder.parse(new InputSource(new StringReader(xml)));
  33.         // 创建XPath对象
  34.         XPathFactory xPathFactory = XPathFactory.newInstance();
  35.         XPath xpath = xPathFactory.newXPath();
  36.         // 设置命名空间上下文,为默认命名空间分配前缀
  37.         NamespaceContext ctx = new DefaultNamespaceContext();
  38.         xpath.setNamespaceContext(ctx);
  39.         // 使用命名空间前缀的XPath表达式
  40.         XPathExpression expr = xpath.compile("//ns:book");
  41.         NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
  42.         // 输出结果
  43.         for (int i = 0; i < nodes.getLength(); i++) {
  44.             System.out.println(nodes.item(i).getTextContent());
  45.         }
  46.     }
  47. }
  48. // 处理默认命名空间的上下文实现
  49. class DefaultNamespaceContext implements NamespaceContext {
  50.     @Override
  51.     public String getNamespaceURI(String prefix) {
  52.         if ("ns".equals(prefix)) {
  53.             return "http://www.example.com/bookstore";
  54.         }
  55.         return null;
  56.     }
  57.     @Override
  58.     public String getPrefix(String namespaceURI) {
  59.         if ("http://www.example.com/bookstore".equals(namespaceURI)) {
  60.             return "ns";
  61.         }
  62.         return null;
  63.     }
  64.     @Override
  65.     public java.util.Iterator<String> getPrefixes(String namespaceURI) {
  66.         java.util.Set<String> prefixes = new java.util.HashSet<String>();
  67.         if ("http://www.example.com/bookstore".equals(namespaceURI)) {
  68.             prefixes.add("ns");
  69.         }
  70.         return prefixes.iterator();
  71.     }
  72. }
复制代码
  1. from lxml import etree
  2. xml = """
  3. <bookstore xmlns="http://www.example.com/bookstore">
  4.     <book category="cooking">
  5.         <title lang="en">Everyday Italian</title>
  6.         <author>Giada De Laurentiis</author>
  7.         <year>2005</year>
  8.         <price>30.00</price>
  9.     </book>
  10.     <book category="children">
  11.         <title lang="en">Harry Potter</title>
  12.         <author>J.K. Rowling</author>
  13.         <year>2005</year>
  14.         <price>29.99</price>
  15.     </book>
  16. </bookstore>
  17. """
  18. # 解析XML
  19. doc = etree.fromstring(xml)
  20. # 定义命名空间映射,为默认命名空间分配前缀
  21. ns = {
  22.     'ns': 'http://www.example.com/bookstore'
  23. }
  24. # 使用命名空间前缀的XPath表达式
  25. books = doc.xpath('//ns:book', namespaces=ns)
  26. # 输出结果
  27. for book in books:
  28.     print(etree.tostring(book, encoding='unicode'))
复制代码

高级命名空间处理技巧

处理动态命名空间

有时,XML文档中的命名空间URI可能是动态生成的,或者在不同文档中有所不同。在这种情况下,我们可以使用XPath函数来处理命名空间。

local-name()函数返回节点的本地名称(不带命名空间前缀),这使我们可以忽略命名空间进行匹配。
  1. <!-- 动态命名空间示例 -->
  2. <bookstore xmlns:ns1="http://www.example.com/12345">
  3.     <ns1:book category="cooking">
  4.         <ns1:title lang="en">Everyday Italian</ns1:title>
  5.     </ns1:book>
  6. </bookstore>
复制代码
  1. import javax.xml.parsers.DocumentBuilder;
  2. import javax.xml.parsers.DocumentBuilderFactory;
  3. import javax.xml.xpath.XPath;
  4. import javax.xml.xpath.XPathConstants;
  5. import javax.xml.xpath.XPathExpression;
  6. import javax.xml.xpath.XPathFactory;
  7. import org.w3c.dom.Document;
  8. import org.w3c.dom.NodeList;
  9. import java.io.StringReader;
  10. import org.xml.sax.InputSource;
  11. public class XPathDynamicNamespaceExample {
  12.     public static void main(String[] args) throws Exception {
  13.         String xml = "<bookstore xmlns:ns1="http://www.example.com/12345">" +
  14.                      "    <ns1:book category="cooking">" +
  15.                      "        <ns1:title lang="en">Everyday Italian</ns1:title>" +
  16.                      "    </ns1:book>" +
  17.                      "</bookstore>";
  18.         // 创建DOM文档
  19.         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  20.         factory.setNamespaceAware(true);
  21.         DocumentBuilder builder = factory.newDocumentBuilder();
  22.         Document doc = builder.parse(new InputSource(new StringReader(xml)));
  23.         // 创建XPath对象
  24.         XPathFactory xPathFactory = XPathFactory.newInstance();
  25.         XPath xpath = xPathFactory.newXPath();
  26.         // 使用local-name()函数忽略命名空间
  27.         XPathExpression expr = xpath.compile("//*[local-name()='book']");
  28.         NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
  29.         // 输出结果
  30.         for (int i = 0; i < nodes.getLength(); i++) {
  31.             System.out.println(nodes.item(i).getTextContent());
  32.         }
  33.     }
  34. }
复制代码
  1. from lxml import etree
  2. xml = """
  3. <bookstore xmlns:ns1="http://www.example.com/12345">
  4.     <ns1:book category="cooking">
  5.         <ns1:title lang="en">Everyday Italian</ns1:title>
  6.     </ns1:book>
  7. </bookstore>
  8. """
  9. # 解析XML
  10. doc = etree.fromstring(xml)
  11. # 使用local-name()函数忽略命名空间
  12. books = doc.xpath("//*[local-name()='book']")
  13. # 输出结果
  14. for book in books:
  15.     print(etree.tostring(book, encoding='unicode'))
复制代码

namespace-uri()函数返回节点的命名空间URI,这使我们可以根据命名空间URI进行匹配。
  1. import javax.xml.parsers.DocumentBuilder;
  2. import javax.xml.parsers.DocumentBuilderFactory;
  3. import javax.xml.xpath.XPath;
  4. import javax.xml.xpath.XPathConstants;
  5. import javax.xml.xpath.XPathExpression;
  6. import javax.xml.xpath.XPathFactory;
  7. import org.w3c.dom.Document;
  8. import org.w3c.dom.NodeList;
  9. import java.io.StringReader;
  10. import org.xml.sax.InputSource;
  11. public class XPathNamespaceURIExample {
  12.     public static void main(String[] args) throws Exception {
  13.         String xml = "<bookstore xmlns:ns1="http://www.example.com/12345">" +
  14.                      "    <ns1:book category="cooking">" +
  15.                      "        <ns1:title lang="en">Everyday Italian</ns1:title>" +
  16.                      "    </ns1:book>" +
  17.                      "</bookstore>";
  18.         // 创建DOM文档
  19.         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  20.         factory.setNamespaceAware(true);
  21.         DocumentBuilder builder = factory.newDocumentBuilder();
  22.         Document doc = builder.parse(new InputSource(new StringReader(xml)));
  23.         // 创建XPath对象
  24.         XPathFactory xPathFactory = XPathFactory.newInstance();
  25.         XPath xpath = xPathFactory.newXPath();
  26.         // 使用namespace-uri()函数匹配命名空间
  27.         XPathExpression expr = xpath.compile("//*[namespace-uri()='http://www.example.com/12345']");
  28.         NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
  29.         // 输出结果
  30.         for (int i = 0; i < nodes.getLength(); i++) {
  31.             System.out.println(nodes.item(i).getTextContent());
  32.         }
  33.     }
  34. }
复制代码
  1. from lxml import etree
  2. xml = """
  3. <bookstore xmlns:ns1="http://www.example.com/12345">
  4.     <ns1:book category="cooking">
  5.         <ns1:title lang="en">Everyday Italian</ns1:title>
  6.     </ns1:book>
  7. </bookstore>
  8. """
  9. # 解析XML
  10. doc = etree.fromstring(xml)
  11. # 使用namespace-uri()函数匹配命名空间
  12. books = doc.xpath("//*[namespace-uri()='http://www.example.com/12345']")
  13. # 输出结果
  14. for book in books:
  15.     print(etree.tostring(book, encoding='unicode'))
复制代码

处理嵌套命名空间

当XML文档中包含嵌套的命名空间声明时,我们需要特别注意命名空间的作用域。
  1. <root xmlns="http://www.example.com/root">
  2.     <child xmlns="http://www.example.com/child">
  3.         <grandchild xmlns="http://www.example.com/grandchild">
  4.             Content
  5.         </grandchild>
  6.     </child>
  7. </root>
复制代码

在这种情况下,每个元素都属于不同的命名空间,即使它们使用了相同的本地名称。
  1. import javax.xml.parsers.DocumentBuilder;
  2. import javax.xml.parsers.DocumentBuilderFactory;
  3. import javax.xml.xpath.XPath;
  4. import javax.xml.xpath.XPathConstants;
  5. import javax.xml.xpath.XPathExpression;
  6. import javax.xml.xpath.XPathFactory;
  7. import javax.xml.namespace.NamespaceContext;
  8. import org.w3c.dom.Document;
  9. import org.w3c.dom.NodeList;
  10. import java.io.StringReader;
  11. import org.xml.sax.InputSource;
  12. public class XPathNestedNamespaceExample {
  13.     public static void main(String[] args) throws Exception {
  14.         String xml = "<root xmlns="http://www.example.com/root">" +
  15.                      "    <child xmlns="http://www.example.com/child">" +
  16.                      "        <grandchild xmlns="http://www.example.com/grandchild">" +
  17.                      "            Content" +
  18.                      "        </grandchild>" +
  19.                      "    </child>" +
  20.                      "</root>";
  21.         // 创建DOM文档
  22.         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  23.         factory.setNamespaceAware(true);
  24.         DocumentBuilder builder = factory.newDocumentBuilder();
  25.         Document doc = builder.parse(new InputSource(new StringReader(xml)));
  26.         // 创建XPath对象
  27.         XPathFactory xPathFactory = XPathFactory.newInstance();
  28.         XPath xpath = xPathFactory.newXPath();
  29.         // 设置命名空间上下文
  30.         NamespaceContext ctx = new NestedNamespaceContext();
  31.         xpath.setNamespaceContext(ctx);
  32.         // 使用命名空间前缀的XPath表达式
  33.         XPathExpression expr = xpath.compile("//root:child/child:grandchild");
  34.         NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
  35.         // 输出结果
  36.         for (int i = 0; i < nodes.getLength(); i++) {
  37.             System.out.println(nodes.item(i).getTextContent());
  38.         }
  39.     }
  40. }
  41. // 处理嵌套命名空间的上下文实现
  42. class NestedNamespaceContext implements NamespaceContext {
  43.     @Override
  44.     public String getNamespaceURI(String prefix) {
  45.         if ("root".equals(prefix)) {
  46.             return "http://www.example.com/root";
  47.         } else if ("child".equals(prefix)) {
  48.             return "http://www.example.com/child";
  49.         } else if ("grandchild".equals(prefix)) {
  50.             return "http://www.example.com/grandchild";
  51.         }
  52.         return null;
  53.     }
  54.     @Override
  55.     public String getPrefix(String namespaceURI) {
  56.         if ("http://www.example.com/root".equals(namespaceURI)) {
  57.             return "root";
  58.         } else if ("http://www.example.com/child".equals(namespaceURI)) {
  59.             return "child";
  60.         } else if ("http://www.example.com/grandchild".equals(namespaceURI)) {
  61.             return "grandchild";
  62.         }
  63.         return null;
  64.     }
  65.     @Override
  66.     public java.util.Iterator<String> getPrefixes(String namespaceURI) {
  67.         java.util.Set<String> prefixes = new java.util.HashSet<String>();
  68.         if ("http://www.example.com/root".equals(namespaceURI)) {
  69.             prefixes.add("root");
  70.         } else if ("http://www.example.com/child".equals(namespaceURI)) {
  71.             prefixes.add("child");
  72.         } else if ("http://www.example.com/grandchild".equals(namespaceURI)) {
  73.             prefixes.add("grandchild");
  74.         }
  75.         return prefixes.iterator();
  76.     }
  77. }
复制代码
  1. from lxml import etree
  2. xml = """
  3. <root xmlns="http://www.example.com/root">
  4.     <child xmlns="http://www.example.com/child">
  5.         <grandchild xmlns="http://www.example.com/grandchild">
  6.             Content
  7.         </grandchild>
  8.     </child>
  9. </root>
  10. """
  11. # 解析XML
  12. doc = etree.fromstring(xml)
  13. # 定义命名空间映射
  14. ns = {
  15.     'root': 'http://www.example.com/root',
  16.     'child': 'http://www.example.com/child',
  17.     'grandchild': 'http://www.example.com/grandchild'
  18. }
  19. # 使用命名空间前缀的XPath表达式
  20. grandchildren = doc.xpath("//root:child/child:grandchild", namespaces=ns)
  21. # 输出结果
  22. for grandchild in grandchildren:
  23.     print(grandchild.text)
复制代码

忽略命名空间的技巧

在某些情况下,我们可能希望完全忽略命名空间,直接根据本地名称匹配元素。虽然这不是最佳实践,但在某些特定场景下可能会很有用。
  1. import javax.xml.parsers.DocumentBuilder;
  2. import javax.xml.parsers.DocumentBuilderFactory;
  3. import javax.xml.xpath.XPath;
  4. import javax.xml.xpath.XPathConstants;
  5. import javax.xml.xpath.XPathExpression;
  6. import javax.xml.xpath.XPathFactory;
  7. import org.w3c.dom.Document;
  8. import org.w3c.dom.NodeList;
  9. import java.io.StringReader;
  10. import org.xml.sax.InputSource;
  11. public class XPathIgnoreNamespaceExample {
  12.     public static void main(String[] args) throws Exception {
  13.         String xml = "<bookstore xmlns:book="http://www.example.com/books">" +
  14.                      "    <book:book category="cooking">" +
  15.                      "        <book:title lang="en">Everyday Italian</book:title>" +
  16.                      "    </book:book>" +
  17.                      "</bookstore>";
  18.         // 创建DOM文档
  19.         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  20.         factory.setNamespaceAware(true);
  21.         DocumentBuilder builder = factory.newDocumentBuilder();
  22.         Document doc = builder.parse(new InputSource(new StringReader(xml)));
  23.         // 创建XPath对象
  24.         XPathFactory xPathFactory = XPathFactory.newInstance();
  25.         XPath xpath = xPathFactory.newXPath();
  26.         // 使用local-name()函数忽略命名空间
  27.         XPathExpression expr = xpath.compile("//*[local-name()='book']");
  28.         NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
  29.         // 输出结果
  30.         for (int i = 0; i < nodes.getLength(); i++) {
  31.             System.out.println(nodes.item(i).getTextContent());
  32.         }
  33.     }
  34. }
复制代码
  1. from lxml import etree
  2. xml = """
  3. <bookstore xmlns:book="http://www.example.com/books">
  4.     <book:book category="cooking">
  5.         <book:title lang="en">Everyday Italian</book:title>
  6.     </book:book>
  7. </bookstore>
  8. """
  9. # 解析XML
  10. doc = etree.fromstring(xml)
  11. # 使用local-name()函数忽略命名空间
  12. books = doc.xpath("//*[local-name()='book']")
  13. # 输出结果
  14. for book in books:
  15.     print(etree.tostring(book, encoding='unicode'))
复制代码

不同编程语言中的实现

Java中的实现

Java提供了多种处理XML和XPath的API,包括DOM、JDOM、DOM4J等。下面展示几种常见的实现方式。
  1. import javax.xml.parsers.DocumentBuilder;
  2. import javax.xml.parsers.DocumentBuilderFactory;
  3. import javax.xml.xpath.XPath;
  4. import javax.xml.xpath.XPathConstants;
  5. import javax.xml.xpath.XPathExpression;
  6. import javax.xml.xpath.XPathFactory;
  7. import javax.xml.namespace.NamespaceContext;
  8. import org.w3c.dom.Document;
  9. import org.w3c.dom.NodeList;
  10. import java.io.StringReader;
  11. import org.xml.sax.InputSource;
  12. public class JavaDomXPathExample {
  13.     public static void main(String[] args) throws Exception {
  14.         String xml = "<bookstore xmlns:book="http://www.example.com/books" xmlns:auth="http://www.example.com/authors">" +
  15.                      "    <book:book category="cooking">" +
  16.                      "        <book:title lang="en">Everyday Italian</book:title>" +
  17.                      "        <auth:author>Giada De Laurentiis</auth:author>" +
  18.                      "        <book:year>2005</book:year>" +
  19.                      "        <book:price>30.00</book:price>" +
  20.                      "    </book:book>" +
  21.                      "    <book:book category="children">" +
  22.                      "        <book:title lang="en">Harry Potter</book:title>" +
  23.                      "        <auth:author>J.K. Rowling</auth:author>" +
  24.                      "        <book:year>2005</book:year>" +
  25.                      "        <book:price>29.99</book:price>" +
  26.                      "    </book:book>" +
  27.                      "</bookstore>";
  28.         // 创建DOM文档
  29.         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  30.         factory.setNamespaceAware(true);
  31.         DocumentBuilder builder = factory.newDocumentBuilder();
  32.         Document doc = builder.parse(new InputSource(new StringReader(xml)));
  33.         // 创建XPath对象
  34.         XPathFactory xPathFactory = XPathFactory.newInstance();
  35.         XPath xpath = xPathFactory.newXPath();
  36.         // 设置命名空间上下文
  37.         NamespaceContext ctx = new BookNamespaceContext();
  38.         xpath.setNamespaceContext(ctx);
  39.         // 查询所有书籍
  40.         XPathExpression expr = xpath.compile("//book:book");
  41.         NodeList books = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
  42.         // 输出结果
  43.         for (int i = 0; i < books.getLength(); i++) {
  44.             System.out.println("Book " + (i + 1) + ":");
  45.             
  46.             // 获取标题
  47.             expr = xpath.compile("book:title", books.item(i));
  48.             String title = (String) expr.evaluate(books.item(i), XPathConstants.STRING);
  49.             System.out.println("  Title: " + title);
  50.             
  51.             // 获取作者
  52.             expr = xpath.compile("auth:author", books.item(i));
  53.             String author = (String) expr.evaluate(books.item(i), XPathConstants.STRING);
  54.             System.out.println("  Author: " + author);
  55.             
  56.             // 获取价格
  57.             expr = xpath.compile("book:price", books.item(i));
  58.             String price = (String) expr.evaluate(books.item(i), XPathConstants.STRING);
  59.             System.out.println("  Price: " + price);
  60.             
  61.             System.out.println();
  62.         }
  63.     }
  64. }
  65. class BookNamespaceContext implements NamespaceContext {
  66.     @Override
  67.     public String getNamespaceURI(String prefix) {
  68.         if ("book".equals(prefix)) {
  69.             return "http://www.example.com/books";
  70.         } else if ("auth".equals(prefix)) {
  71.             return "http://www.example.com/authors";
  72.         }
  73.         return null;
  74.     }
  75.     @Override
  76.     public String getPrefix(String namespaceURI) {
  77.         if ("http://www.example.com/books".equals(namespaceURI)) {
  78.             return "book";
  79.         } else if ("http://www.example.com/authors".equals(namespaceURI)) {
  80.             return "auth";
  81.         }
  82.         return null;
  83.     }
  84.     @Override
  85.     public java.util.Iterator<String> getPrefixes(String namespaceURI) {
  86.         java.util.Set<String> prefixes = new java.util.HashSet<String>();
  87.         if ("http://www.example.com/books".equals(namespaceURI)) {
  88.             prefixes.add("book");
  89.         } else if ("http://www.example.com/authors".equals(namespaceURI)) {
  90.             prefixes.add("auth");
  91.         }
  92.         return prefixes.iterator();
  93.     }
  94. }
复制代码
  1. import org.jdom2.Document;
  2. import org.jdom2.Element;
  3. import org.jdom2.Namespace;
  4. import org.jdom2.input.SAXBuilder;
  5. import org.jdom2.xpath.XPathFactory;
  6. import org.jdom2.xpath.XPathExpression;
  7. import java.io.StringReader;
  8. import java.util.List;
  9. public class JavaJdomXPathExample {
  10.     public static void main(String[] args) throws Exception {
  11.         String xml = "<bookstore xmlns:book="http://www.example.com/books" xmlns:auth="http://www.example.com/authors">" +
  12.                      "    <book:book category="cooking">" +
  13.                      "        <book:title lang="en">Everyday Italian</book:title>" +
  14.                      "        <auth:author>Giada De Laurentiis</auth:author>" +
  15.                      "        <book:year>2005</book:year>" +
  16.                      "        <book:price>30.00</book:price>" +
  17.                      "    </book:book>" +
  18.                      "    <book:book category="children">" +
  19.                      "        <book:title lang="en">Harry Potter</book:title>" +
  20.                      "        <auth:author>J.K. Rowling</auth:author>" +
  21.                      "        <book:year>2005</book:year>" +
  22.                      "        <book:price>29.99</book:price>" +
  23.                      "    </book:book>" +
  24.                      "</bookstore>";
  25.         // 解析XML
  26.         SAXBuilder builder = new SAXBuilder();
  27.         Document doc = builder.build(new StringReader(xml));
  28.         // 定义命名空间
  29.         Namespace bookNs = Namespace.getNamespace("book", "http://www.example.com/books");
  30.         Namespace authNs = Namespace.getNamespace("auth", "http://www.example.com/authors");
  31.         // 创建XPath表达式
  32.         XPathExpression<Element> expr = XPathFactory.instance().compile("//book:book",
  33.             new org.jdom2.filter.ElementFilter(), null, bookNs);
  34.         // 执行查询
  35.         List<Element> books = expr.evaluate(doc);
  36.         // 输出结果
  37.         for (int i = 0; i < books.size(); i++) {
  38.             Element book = books.get(i);
  39.             System.out.println("Book " + (i + 1) + ":");
  40.             
  41.             // 获取标题
  42.             Element title = book.getChild("title", bookNs);
  43.             System.out.println("  Title: " + title.getText());
  44.             
  45.             // 获取作者
  46.             Element author = book.getChild("author", authNs);
  47.             System.out.println("  Author: " + author.getText());
  48.             
  49.             // 获取价格
  50.             Element price = book.getChild("price", bookNs);
  51.             System.out.println("  Price: " + price.getText());
  52.             
  53.             System.out.println();
  54.         }
  55.     }
  56. }
复制代码

Python中的实现

Python提供了多种处理XML的库,包括lxml、ElementTree、minidom等。下面展示几种常见的实现方式。
  1. from lxml import etree
  2. xml = """
  3. <bookstore xmlns:book="http://www.example.com/books" xmlns:auth="http://www.example.com/authors">
  4.     <book:book category="cooking">
  5.         <book:title lang="en">Everyday Italian</book:title>
  6.         <auth:author>Giada De Laurentiis</auth:author>
  7.         <book:year>2005</book:year>
  8.         <book:price>30.00</book:price>
  9.     </book:book>
  10.     <book:book category="children">
  11.         <book:title lang="en">Harry Potter</book:title>
  12.         <auth:author>J.K. Rowling</auth:author>
  13.         <book:year>2005</book:year>
  14.         <book:price>29.99</book:price>
  15.     </book:book>
  16. </bookstore>
  17. """
  18. # 解析XML
  19. doc = etree.fromstring(xml)
  20. # 定义命名空间映射
  21. ns = {
  22.     'book': 'http://www.example.com/books',
  23.     'auth': 'http://www.example.com/authors'
  24. }
  25. # 查询所有书籍
  26. books = doc.xpath('//book:book', namespaces=ns)
  27. # 输出结果
  28. for i, book in enumerate(books, 1):
  29.     print(f"Book {i}:")
  30.    
  31.     # 获取标题
  32.     title = book.xpath('book:title/text()', namespaces=ns)[0]
  33.     print(f"  Title: {title}")
  34.    
  35.     # 获取作者
  36.     author = book.xpath('auth:author/text()', namespaces=ns)[0]
  37.     print(f"  Author: {author}")
  38.    
  39.     # 获取价格
  40.     price = book.xpath('book:price/text()', namespaces=ns)[0]
  41.     print(f"  Price: {price}")
  42.    
  43.     print()
复制代码
  1. import xml.etree.ElementTree as ET
  2. xml = """
  3. <bookstore xmlns:book="http://www.example.com/books" xmlns:auth="http://www.example.com/authors">
  4.     <book:book category="cooking">
  5.         <book:title lang="en">Everyday Italian</book:title>
  6.         <auth:author>Giada De Laurentiis</auth:author>
  7.         <book:year>2005</book:year>
  8.         <book:price>30.00</book:price>
  9.     </book:book>
  10.     <book:book category="children">
  11.         <book:title lang="en">Harry Potter</book:title>
  12.         <auth:author>J.K. Rowling</auth:author>
  13.         <book:year>2005</book:year>
  14.         <book:price>29.99</book:price>
  15.     </book:book>
  16. </bookstore>
  17. """
  18. # 解析XML
  19. root = ET.fromstring(xml)
  20. # 定义命名空间映射
  21. ns = {
  22.     'book': 'http://www.example.com/books',
  23.     'auth': 'http://www.example.com/authors'
  24. }
  25. # 查询所有书籍
  26. books = root.findall('.//book:book', ns)
  27. # 输出结果
  28. for i, book in enumerate(books, 1):
  29.     print(f"Book {i}:")
  30.    
  31.     # 获取标题
  32.     title = book.find('book:title', ns).text
  33.     print(f"  Title: {title}")
  34.    
  35.     # 获取作者
  36.     author = book.find('auth:author', ns).text
  37.     print(f"  Author: {author}")
  38.    
  39.     # 获取价格
  40.     price = book.find('book:price', ns).text
  41.     print(f"  Price: {price}")
  42.    
  43.     print()
复制代码

C#中的实现

C#提供了System.Xml命名空间来处理XML和XPath。
  1. using System;
  2. using System.Xml;
  3. using System.Xml.XPath;
  4. class CSharpXPathExample
  5. {
  6.     static void Main(string[] args)
  7.     {
  8.         string xml = @"<bookstore xmlns:book=""http://www.example.com/books"" xmlns:auth=""http://www.example.com/authors"">
  9.                         <book:book category=""cooking"">
  10.                             <book:title lang=""en"">Everyday Italian</book:title>
  11.                             <auth:author>Giada De Laurentiis</auth:author>
  12.                             <book:year>2005</book:year>
  13.                             <book:price>30.00</book:price>
  14.                         </book:book>
  15.                         <book:book category=""children"">
  16.                             <book:title lang=""en"">Harry Potter</book:title>
  17.                             <auth:author>J.K. Rowling</auth:author>
  18.                             <book:year>2005</book:year>
  19.                             <book:price>29.99</book:price>
  20.                         </book:book>
  21.                       </bookstore>";
  22.         // 创建XmlDocument
  23.         XmlDocument doc = new XmlDocument();
  24.         doc.LoadXml(xml);
  25.         // 创建XmlNamespaceManager
  26.         XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable);
  27.         nsMgr.AddNamespace("book", "http://www.example.com/books");
  28.         nsMgr.AddNamespace("auth", "http://www.example.com/authors");
  29.         // 查询所有书籍
  30.         XmlNodeList books = doc.SelectNodes("//book:book", nsMgr);
  31.         // 输出结果
  32.         for (int i = 0; i < books.Count; i++)
  33.         {
  34.             Console.WriteLine($"Book {i + 1}:");
  35.             
  36.             // 获取标题
  37.             XmlNode title = books[i].SelectSingleNode("book:title", nsMgr);
  38.             Console.WriteLine($"  Title: {title.InnerText}");
  39.             
  40.             // 获取作者
  41.             XmlNode author = books[i].SelectSingleNode("auth:author", nsMgr);
  42.             Console.WriteLine($"  Author: {author.InnerText}");
  43.             
  44.             // 获取价格
  45.             XmlNode price = books[i].SelectSingleNode("book:price", nsMgr);
  46.             Console.WriteLine($"  Price: {price.InnerText}");
  47.             
  48.             Console.WriteLine();
  49.         }
  50.     }
  51. }
复制代码

JavaScript中的实现

JavaScript可以在浏览器环境中使用DOM API,或者在Node.js环境中使用第三方库如xmldom来处理XML。
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <title>XPath XML Namespace Example</title>
  5. </head>
  6. <body>
  7.     <script>
  8.         // XML字符串
  9.         const xml = `<bookstore xmlns:book="http://www.example.com/books" xmlns:auth="http://www.example.com/authors">
  10.                         <book:book category="cooking">
  11.                             <book:title lang="en">Everyday Italian</book:title>
  12.                             <auth:author>Giada De Laurentiis</auth:author>
  13.                             <book:year>2005</book:year>
  14.                             <book:price>30.00</book:price>
  15.                         </book:book>
  16.                         <book:book category="children">
  17.                             <book:title lang="en">Harry Potter</book:title>
  18.                             <auth:author>J.K. Rowling</auth:author>
  19.                             <book:year>2005</book:year>
  20.                             <book:price>29.99</book:price>
  21.                         </book:book>
  22.                      </bookstore>`;
  23.         // 解析XML
  24.         const parser = new DOMParser();
  25.         const doc = parser.parseFromString(xml, "application/xml");
  26.         // 创建XPath解析器
  27.         const resolver = {
  28.             lookupNamespaceURI: function(prefix) {
  29.                 const namespaces = {
  30.                     'book': 'http://www.example.com/books',
  31.                     'auth': 'http://www.example.com/authors'
  32.                 };
  33.                 return namespaces[prefix] || null;
  34.             }
  35.         };
  36.         // 查询所有书籍
  37.         const books = doc.evaluate('//book:book', doc, resolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  38.         // 输出结果
  39.         for (let i = 0; i < books.snapshotLength; i++) {
  40.             const book = books.snapshotItem(i);
  41.             console.log(`Book ${i + 1}:`);
  42.             
  43.             // 获取标题
  44.             const title = doc.evaluate('book:title', book, resolver, XPathResult.STRING_TYPE, null).stringValue;
  45.             console.log(`  Title: ${title}`);
  46.             
  47.             // 获取作者
  48.             const author = doc.evaluate('auth:author', book, resolver, XPathResult.STRING_TYPE, null).stringValue;
  49.             console.log(`  Author: ${author}`);
  50.             
  51.             // 获取价格
  52.             const price = doc.evaluate('book:price', book, resolver, XPathResult.STRING_TYPE, null).stringValue;
  53.             console.log(`  Price: ${price}`);
  54.             
  55.             console.log('');
  56.         }
  57.     </script>
  58. </body>
  59. </html>
复制代码
  1. const { DOMParser, XPathEvaluator } = require('xmldom');
  2. // XML字符串
  3. const xml = `<bookstore xmlns:book="http://www.example.com/books" xmlns:auth="http://www.example.com/authors">
  4.                 <book:book category="cooking">
  5.                     <book:title lang="en">Everyday Italian</book:title>
  6.                     <auth:author>Giada De Laurentiis</auth:author>
  7.                     <book:year>2005</book:year>
  8.                     <book:price>30.00</book:price>
  9.                 </book:book>
  10.                 <book:book category="children">
  11.                     <book:title lang="en">Harry Potter</book:title>
  12.                     <auth:author>J.K. Rowling</auth:author>
  13.                     <book:year>2005</book:year>
  14.                     <book:price>29.99</book:price>
  15.                 </book:book>
  16.              </bookstore>`;
  17. // 解析XML
  18. const doc = new DOMParser().parseFromString(xml);
  19. // 创建命名空间解析器
  20. const resolver = {
  21.     lookupNamespaceURI: function(prefix) {
  22.         const namespaces = {
  23.             'book': 'http://www.example.com/books',
  24.             'auth': 'http://www.example.com/authors'
  25.         };
  26.         return namespaces[prefix] || null;
  27.     }
  28. };
  29. // 创建XPath评估器
  30. const evaluator = new XPathEvaluator();
  31. // 查询所有书籍
  32. const books = evaluator.evaluate('//book:book', doc, resolver, XPathEvaluator.ORDERED_NODE_SNAPSHOT_TYPE, null);
  33. // 输出结果
  34. for (let i = 0; i < books.snapshotLength; i++) {
  35.     const book = books.snapshotItem(i);
  36.     console.log(`Book ${i + 1}:`);
  37.    
  38.     // 获取标题
  39.     const title = evaluator.evaluate('book:title', book, resolver, XPathEvaluator.STRING_TYPE, null).stringValue;
  40.     console.log(`  Title: ${title}`);
  41.    
  42.     // 获取作者
  43.     const author = evaluator.evaluate('auth:author', book, resolver, XPathEvaluator.STRING_TYPE, null).stringValue;
  44.     console.log(`  Author: ${author}`);
  45.    
  46.     // 获取价格
  47.     const price = evaluator.evaluate('book:price', book, resolver, XPathEvaluator.STRING_TYPE, null).stringValue;
  48.     console.log(`  Price: ${price}`);
  49.    
  50.     console.log('');
  51. }
复制代码

实际应用场景和案例

Web服务响应处理

Web服务(特别是SOAP服务)经常使用带有命名空间的XML作为响应格式。处理这些响应时,正确处理命名空间至关重要。
  1. <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
  2.                xmlns:m="http://www.example.com/bookstore">
  3.     <soap:Body>
  4.         <m:GetBookResponse>
  5.             <m:Book>
  6.                 <m:Title>XML Guide</m:Title>
  7.                 <m:Author>John Doe</m:Author>
  8.                 <m:Price>29.99</m:Price>
  9.             </m:Book>
  10.         </m:GetBookResponse>
  11.     </soap:Body>
  12. </soap:Envelope>
复制代码
  1. import javax.xml.parsers.DocumentBuilder;
  2. import javax.xml.parsers.DocumentBuilderFactory;
  3. import javax.xml.xpath.XPath;
  4. import javax.xml.xpath.XPathConstants;
  5. import javax.xml.xpath.XPathExpression;
  6. import javax.xml.xpath.XPathFactory;
  7. import javax.xml.namespace.NamespaceContext;
  8. import org.w3c.dom.Document;
  9. import java.io.StringReader;
  10. import org.xml.sax.InputSource;
  11. public class SoapResponseExample {
  12.     public static void main(String[] args) throws Exception {
  13.         String soapResponse = "<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"\n" +
  14.                               "               xmlns:m="http://www.example.com/bookstore">\n" +
  15.                               "    <soap:Body>\n" +
  16.                               "        <m:GetBookResponse>\n" +
  17.                               "            <m:Book>\n" +
  18.                               "                <m:Title>XML Guide</m:Title>\n" +
  19.                               "                <m:Author>John Doe</m:Author>\n" +
  20.                               "                <m:Price>29.99</m:Price>\n" +
  21.                               "            </m:Book>\n" +
  22.                               "        </m:GetBookResponse>\n" +
  23.                               "    </soap:Body>\n" +
  24.                               "</soap:Envelope>";
  25.         // 创建DOM文档
  26.         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  27.         factory.setNamespaceAware(true);
  28.         DocumentBuilder builder = factory.newDocumentBuilder();
  29.         Document doc = builder.parse(new InputSource(new StringReader(soapResponse)));
  30.         // 创建XPath对象
  31.         XPathFactory xPathFactory = XPathFactory.newInstance();
  32.         XPath xpath = xPathFactory.newXPath();
  33.         // 设置命名空间上下文
  34.         NamespaceContext ctx = new SoapNamespaceContext();
  35.         xpath.setNamespaceContext(ctx);
  36.         // 提取书名
  37.         XPathExpression expr = xpath.compile("//m:Title");
  38.         String title = (String) expr.evaluate(doc, XPathConstants.STRING);
  39.         System.out.println("Title: " + title);
  40.         // 提取作者
  41.         expr = xpath.compile("//m:Author");
  42.         String author = (String) expr.evaluate(doc, XPathConstants.STRING);
  43.         System.out.println("Author: " + author);
  44.         // 提取价格
  45.         expr = xpath.compile("//m:Price");
  46.         String price = (String) expr.evaluate(doc, XPathConstants.STRING);
  47.         System.out.println("Price: " + price);
  48.     }
  49. }
  50. class SoapNamespaceContext implements NamespaceContext {
  51.     @Override
  52.     public String getNamespaceURI(String prefix) {
  53.         if ("soap".equals(prefix)) {
  54.             return "http://www.w3.org/2003/05/soap-envelope";
  55.         } else if ("m".equals(prefix)) {
  56.             return "http://www.example.com/bookstore";
  57.         }
  58.         return null;
  59.     }
  60.     @Override
  61.     public String getPrefix(String namespaceURI) {
  62.         if ("http://www.w3.org/2003/05/soap-envelope".equals(namespaceURI)) {
  63.             return "soap";
  64.         } else if ("http://www.example.com/bookstore".equals(namespaceURI)) {
  65.             return "m";
  66.         }
  67.         return null;
  68.     }
  69.     @Override
  70.     public java.util.Iterator<String> getPrefixes(String namespaceURI) {
  71.         java.util.Set<String> prefixes = new java.util.HashSet<String>();
  72.         if ("http://www.w3.org/2003/05/soap-envelope".equals(namespaceURI)) {
  73.             prefixes.add("soap");
  74.         } else if ("http://www.example.com/bookstore".equals(namespaceURI)) {
  75.             prefixes.add("m");
  76.         }
  77.         return prefixes.iterator();
  78.     }
  79. }
复制代码

配置文件解析

许多应用程序使用XML作为配置文件格式,这些文件通常使用命名空间来组织和区分不同模块的配置。
  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3.        xmlns:context="http://www.springframework.org/schema/context"
  4.        xsi:schemaLocation="http://www.springframework.org/schema/beans
  5.                            http://www.springframework.org/schema/beans/spring-beans.xsd
  6.                            http://www.springframework.org/schema/context
  7.                            http://www.springframework.org/schema/context/spring-context.xsd">
  8.    
  9.     <context:component-scan base-package="com.example"/>
  10.    
  11.     <bean id="userService" class="com.example.UserService"/>
  12.    
  13.     <bean id="dataSource" class="com.example.DataSource">
  14.         <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
  15.         <property name="username" value="root"/>
  16.         <property name="password" value="password"/>
  17.     </bean>
  18. </beans>
复制代码
  1. from lxml import etree
  2. # Spring配置文件
  3. spring_config = """
  4. <beans xmlns="http://www.springframework.org/schema/beans"
  5.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  6.        xmlns:context="http://www.springframework.org/schema/context"
  7.        xsi:schemaLocation="http://www.springframework.org/schema/beans
  8.                            http://www.springframework.org/schema/beans/spring-beans.xsd
  9.                            http://www.springframework.org/schema/context
  10.                            http://www.springframework.org/schema/context/spring-context.xsd">
  11.    
  12.     <context:component-scan base-package="com.example"/>
  13.    
  14.     <bean id="userService" class="com.example.UserService"/>
  15.    
  16.     <bean id="dataSource" class="com.example.DataSource">
  17.         <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
  18.         <property name="username" value="root"/>
  19.         <property name="password" value="password"/>
  20.     </bean>
  21. </beans>
  22. """
  23. # 解析XML
  24. doc = etree.fromstring(spring_config)
  25. # 定义命名空间映射
  26. ns = {
  27.     'beans': 'http://www.springframework.org/schema/beans',
  28.     'context': 'http://www.springframework.org/schema/context',
  29.     'xsi': 'http://www.w3.org/2001/XMLSchema-instance'
  30. }
  31. # 获取组件扫描的包
  32. component_scan = doc.xpath('//context:component-scan', namespaces=ns)[0]
  33. base_package = component_scan.get('base-package')
  34. print(f"Component scan base package: {base_package}")
  35. # 获取所有bean定义
  36. beans = doc.xpath('//beans:bean', namespaces=ns)
  37. print("\nBean definitions:")
  38. for bean in beans:
  39.     bean_id = bean.get('id')
  40.     bean_class = bean.get('class')
  41.     print(f"  ID: {bean_id}, Class: {bean_class}")
  42.    
  43.     # 获取bean属性
  44.     properties = bean.xpath('./beans:property', namespaces=ns)
  45.     for prop in properties:
  46.         prop_name = prop.get('name')
  47.         prop_value = prop.get('value')
  48.         print(f"    Property: {prop_name} = {prop_value}")
复制代码

数据转换

在数据转换和集成场景中,我们经常需要从源XML文档中提取数据,并将其转换为目标格式。正确处理命名空间是确保数据准确转换的关键。
  1. <orders xmlns="http://www.example.com/orders"
  2.         xmlns:cust="http://www.example.com/customers"
  3.         xmlns:prod="http://www.example.com/products">
  4.     <order id="1001">
  5.         <cust:customer id="C001">
  6.             <cust:name>John Doe</cust:name>
  7.             <cust:email>john@example.com</cust:email>
  8.         </cust:customer>
  9.         <items>
  10.             <item>
  11.                 <prod:product id="P001">
  12.                     <prod:name>XML Guide</prod:name>
  13.                     <prod:price>29.99</prod:price>
  14.                 </prod:product>
  15.                 <quantity>2</quantity>
  16.             </item>
  17.             <item>
  18.                 <prod:product id="P002">
  19.                     <prod:name>XPath Tutorial</prod:name>
  20.                     <prod:price>19.99</prod:price>
  21.                 </prod:product>
  22.                 <quantity>1</quantity>
  23.             </item>
  24.         </items>
  25.     </order>
  26.     <order id="1002">
  27.         <cust:customer id="C002">
  28.             <cust:name>Jane Smith</cust:name>
  29.             <cust:email>jane@example.com</cust:email>
  30.         </cust:customer>
  31.         <items>
  32.             <item>
  33.                 <prod:product id="P003">
  34.                     <prod:name>Web Services</prod:name>
  35.                     <prod:price>39.99</prod:price>
  36.                 </prod:product>
  37.                 <quantity>1</quantity>
  38.             </item>
  39.         </items>
  40.     </order>
  41. </orders>
复制代码
  1. from lxml import etree
  2. import json
  3. # XML订单数据
  4. orders_xml = """
  5. <orders xmlns="http://www.example.com/orders"
  6.         xmlns:cust="http://www.example.com/customers"
  7.         xmlns:prod="http://www.example.com/products">
  8.     <order id="1001">
  9.         <cust:customer id="C001">
  10.             <cust:name>John Doe</cust:name>
  11.             <cust:email>john@example.com</cust:email>
  12.         </cust:customer>
  13.         <items>
  14.             <item>
  15.                 <prod:product id="P001">
  16.                     <prod:name>XML Guide</prod:name>
  17.                     <prod:price>29.99</prod:price>
  18.                 </prod:product>
  19.                 <quantity>2</quantity>
  20.             </item>
  21.             <item>
  22.                 <prod:product id="P002">
  23.                     <prod:name>XPath Tutorial</prod:name>
  24.                     <prod:price>19.99</prod:price>
  25.                 </prod:product>
  26.                 <quantity>1</quantity>
  27.             </item>
  28.         </items>
  29.     </order>
  30.     <order id="1002">
  31.         <cust:customer id="C002">
  32.             <cust:name>Jane Smith</cust:name>
  33.             <cust:email>jane@example.com</cust:email>
  34.         </cust:customer>
  35.         <items>
  36.             <item>
  37.                 <prod:product id="P003">
  38.                     <prod:name>Web Services</prod:name>
  39.                     <prod:price>39.99</prod:price>
  40.                 </prod:product>
  41.                 <quantity>1</quantity>
  42.             </item>
  43.         </items>
  44.     </order>
  45. </orders>
  46. """
  47. # 解析XML
  48. doc = etree.fromstring(orders_xml)
  49. # 定义命名空间映射
  50. ns = {
  51.     'orders': 'http://www.example.com/orders',
  52.     'cust': 'http://www.example.com/customers',
  53.     'prod': 'http://www.example.com/products'
  54. }
  55. # 转换函数
  56. def xml_orders_to_json(xml_doc, namespaces):
  57.     orders = []
  58.    
  59.     # 获取所有订单
  60.     order_elements = xml_doc.xpath('//orders:order', namespaces=namespaces)
  61.    
  62.     for order_elem in order_elements:
  63.         order_id = order_elem.get('id')
  64.         
  65.         # 获取客户信息
  66.         customer_elem = order_elem.xpath('./cust:customer', namespaces=namespaces)[0]
  67.         customer_id = customer_elem.get('id')
  68.         customer_name = customer_elem.xpath('./cust:name/text()', namespaces=namespaces)[0]
  69.         customer_email = customer_elem.xpath('./cust:email/text()', namespaces=namespaces)[0]
  70.         
  71.         # 获取订单项
  72.         items = []
  73.         item_elements = order_elem.xpath('./items/item', namespaces=namespaces)
  74.         
  75.         for item_elem in item_elements:
  76.             product_elem = item_elem.xpath('./prod:product', namespaces=namespaces)[0]
  77.             product_id = product_elem.get('id')
  78.             product_name = product_elem.xpath('./prod:name/text()', namespaces=namespaces)[0]
  79.             product_price = float(product_elem.xpath('./prod:price/text()', namespaces=namespaces)[0])
  80.             quantity = int(item_elem.xpath('./quantity/text()', namespaces=namespaces)[0])
  81.             
  82.             items.append({
  83.                 'product': {
  84.                     'id': product_id,
  85.                     'name': product_name,
  86.                     'price': product_price
  87.                 },
  88.                 'quantity': quantity
  89.             })
  90.         
  91.         # 构建订单对象
  92.         order = {
  93.             'id': order_id,
  94.             'customer': {
  95.                 'id': customer_id,
  96.                 'name': customer_name,
  97.                 'email': customer_email
  98.             },
  99.             'items': items
  100.         }
  101.         
  102.         orders.append(order)
  103.    
  104.     return orders
  105. # 执行转换
  106. orders_data = xml_orders_to_json(doc, ns)
  107. # 输出JSON
  108. print(json.dumps(orders_data, indent=2))
复制代码

大型XML文档处理

处理大型XML文档时,内存使用和性能成为关键考虑因素。使用SAX(Simple API for XML)或StAX(Streaming API for XML)等流式处理技术可以有效地处理大型文档。
  1. import javax.xml.stream.XMLInputFactory;
  2. import javax.xml.stream.XMLStreamConstants;
  3. import javax.xml.stream.XMLStreamReader;
  4. import java.io.StringReader;
  5. public class LargeXmlProcessingExample {
  6.     public static void main(String[] args) throws Exception {
  7.         String xml = "<orders xmlns="http://www.example.com/orders" xmlns:cust="http://www.example.com/customers">" +
  8.                      "    <order id="1001">" +
  9.                      "        <cust:customer id="C001">" +
  10.                      "            <cust:name>John Doe</cust:name>" +
  11.                      "        </cust:customer>" +
  12.                      "        <items>" +
  13.                      "            <item>" +
  14.                      "                <product>XML Guide</product>" +
  15.                      "                <quantity>2</quantity>" +
  16.                      "            </item>" +
  17.                      "        </items>" +
  18.                      "    </order>" +
  19.                      "    <order id="1002">" +
  20.                      "        <cust:customer id="C002">" +
  21.                      "            <cust:name>Jane Smith</cust:name>" +
  22.                      "        </cust:customer>" +
  23.                      "        <items>" +
  24.                      "            <item>" +
  25.                      "                <product>Web Services</product>" +
  26.                      "                <quantity>1</quantity>" +
  27.                      "            </item>" +
  28.                      "        </items>" +
  29.                      "    </order>" +
  30.                      "</orders>";
  31.         // 创建StAX解析器
  32.         XMLInputFactory factory = XMLInputFactory.newInstance();
  33.         XMLStreamReader reader = factory.createXMLStreamReader(new StringReader(xml));
  34.         // 处理XML文档
  35.         processXml(reader);
  36.         // 关闭解析器
  37.         reader.close();
  38.     }
  39.     private static void processXml(XMLStreamReader reader) throws Exception {
  40.         String currentOrderId = null;
  41.         String currentCustomerId = null;
  42.         String currentCustomerName = null;
  43.         String currentProduct = null;
  44.         String currentQuantity = null;
  45.         while (reader.hasNext()) {
  46.             int event = reader.next();
  47.             switch (event) {
  48.                 case XMLStreamConstants.START_ELEMENT:
  49.                     String elementName = reader.getLocalName();
  50.                     String namespaceUri = reader.getNamespaceURI();
  51.                     // 处理订单开始
  52.                     if ("order".equals(elementName) && "http://www.example.com/orders".equals(namespaceUri)) {
  53.                         currentOrderId = reader.getAttributeValue(null, "id");
  54.                     }
  55.                     // 处理客户开始
  56.                     else if ("customer".equals(elementName) && "http://www.example.com/customers".equals(namespaceUri)) {
  57.                         currentCustomerId = reader.getAttributeValue(null, "id");
  58.                     }
  59.                     break;
  60.                 case XMLStreamConstants.CHARACTERS:
  61.                     String text = reader.getText().trim();
  62.                     if (!text.isEmpty()) {
  63.                         String parentElement = getParentElement(reader);
  64.                         String parentNamespace = getParentNamespace(reader);
  65.                         // 处理客户名称
  66.                         if ("name".equals(parentElement) && "http://www.example.com/customers".equals(parentNamespace)) {
  67.                             currentCustomerName = text;
  68.                         }
  69.                         // 处理产品名称
  70.                         else if ("product".equals(parentElement) && "http://www.example.com/orders".equals(parentNamespace)) {
  71.                             currentProduct = text;
  72.                         }
  73.                         // 处理数量
  74.                         else if ("quantity".equals(parentElement) && "http://www.example.com/orders".equals(parentNamespace)) {
  75.                             currentQuantity = text;
  76.                         }
  77.                     }
  78.                     break;
  79.                 case XMLStreamConstants.END_ELEMENT:
  80.                     String endElementName = reader.getLocalName();
  81.                     String endNamespaceUri = reader.getNamespaceURI();
  82.                     // 处理订单结束
  83.                     if ("order".equals(endElementName) && "http://www.example.com/orders".equals(endNamespaceUri)) {
  84.                         System.out.println("Order ID: " + currentOrderId);
  85.                         System.out.println("Customer ID: " + currentCustomerId);
  86.                         System.out.println("Customer Name: " + currentCustomerName);
  87.                         System.out.println("Product: " + currentProduct);
  88.                         System.out.println("Quantity: " + currentQuantity);
  89.                         System.out.println("----------------------");
  90.                         
  91.                         // 重置变量
  92.                         currentOrderId = null;
  93.                         currentCustomerId = null;
  94.                         currentCustomerName = null;
  95.                         currentProduct = null;
  96.                         currentQuantity = null;
  97.                     }
  98.                     break;
  99.             }
  100.         }
  101.     }
  102.     private static String getParentElement(XMLStreamReader reader) {
  103.         // 简化实现,实际应用中需要维护元素栈
  104.         return null;
  105.     }
  106.     private static String getParentNamespace(XMLStreamReader reader) {
  107.         // 简化实现,实际应用中需要维护命名空间栈
  108.         return null;
  109.     }
  110. }
复制代码

最佳实践和性能优化

命名空间处理的性能考虑

处理XML命名空间时,性能是一个重要的考虑因素,特别是在处理大型XML文档时。以下是一些性能优化的建议:

1. 缓存命名空间上下文:如果多次使用相同的命名空间,应该缓存命名空间上下文对象,避免重复创建。
  1. // Java示例:缓存命名空间上下文
  2. public class NamespaceCache {
  3.     private static final Map<String, NamespaceContext> cache = new HashMap<>();
  4.    
  5.     public static NamespaceContext getNamespaceContext(String key) {
  6.         return cache.get(key);
  7.     }
  8.    
  9.     public static void putNamespaceContext(String key, NamespaceContext ctx) {
  10.         cache.put(key, ctx);
  11.     }
  12. }
  13. // 使用缓存
  14. NamespaceContext ctx = NamespaceCache.getNamespaceContext("bookstore");
  15. if (ctx == null) {
  16.     ctx = new BookNamespaceContext();
  17.     NamespaceCache.putNamespaceContext("bookstore", ctx);
  18. }
  19. xpath.setNamespaceContext(ctx);
复制代码

1. 使用特定的XPath表达式:避免使用过于通用的XPath表达式(如//*),这会导致解析器遍历整个文档树。
  1. // 不好的做法:遍历整个文档
  2. XPathExpression expr = xpath.compile("//*[local-name()='book']");
  3. // 好的做法:使用特定路径
  4. XPathExpression expr = xpath.compile("/bookstore/book");
复制代码

1. 预编译XPath表达式:如果多次使用相同的XPath表达式,应该预编译并缓存这些表达式。
  1. // Java示例:预编译XPath表达式
  2. public class XPathExpressionCache {
  3.     private static final Map<String, XPathExpression> cache = new HashMap<>();
  4.    
  5.     public static XPathExpression getXPathExpression(XPath xpath, String expression) throws Exception {
  6.         XPathExpression expr = cache.get(expression);
  7.         if (expr == null) {
  8.             expr = xpath.compile(expression);
  9.             cache.put(expression, expr);
  10.         }
  11.         return expr;
  12.     }
  13. }
  14. // 使用缓存
  15. XPathExpression expr = XPathExpressionCache.getXPathExpression(xpath, "//book:book");
  16. NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
复制代码

1. 使用适当的XML解析器:根据文档大小和复杂度选择合适的解析器。对于大型文档,考虑使用SAX或StAX等流式解析器。
  1. // 使用DOM解析器(适合小型文档)
  2. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  3. factory.setNamespaceAware(true);
  4. DocumentBuilder builder = factory.newDocumentBuilder();
  5. Document doc = builder.parse(new File("small.xml"));
  6. // 使用StAX解析器(适合大型文档)
  7. XMLInputFactory factory = XMLInputFactory.newInstance();
  8. XMLStreamReader reader = factory.createXMLStreamReader(new FileInputStream("large.xml"));
复制代码

代码组织和可维护性

良好的代码组织和可维护性对于长期维护XPath和XML处理代码至关重要。

1. 封装命名空间处理:创建专门的类来处理命名空间,提高代码的可重用性。
  1. // Java示例:封装命名空间处理
  2. public class NamespaceHandler {
  3.     private final Map<String, String> prefixToUri = new HashMap<>();
  4.     private final Map<String, String> uriToPrefix = new HashMap<>();
  5.    
  6.     public void addNamespace(String prefix, String uri) {
  7.         prefixToUri.put(prefix, uri);
  8.         uriToPrefix.put(uri, prefix);
  9.     }
  10.    
  11.     public NamespaceContext createNamespaceContext() {
  12.         return new SimpleNamespaceContext(prefixToUri, uriToPrefix);
  13.     }
  14.    
  15.     public String getPrefixForUri(String uri) {
  16.         return uriToPrefix.get(uri);
  17.     }
  18.    
  19.     public String getUriForPrefix(String prefix) {
  20.         return prefixToUri.get(prefix);
  21.     }
  22. }
  23. // 使用封装的命名空间处理器
  24. NamespaceHandler nsHandler = new NamespaceHandler();
  25. nsHandler.addNamespace("book", "http://www.example.com/books");
  26. nsHandler.addNamespace("auth", "http://www.example.com/authors");
  27. xpath.setNamespaceContext(nsHandler.createNamespaceContext());
复制代码

1. 使用常量定义XPath表达式:将常用的XPath表达式定义为常量,便于维护和修改。
  1. // Java示例:使用常量定义XPath表达式
  2. public class BookXPathExpressions {
  3.     public static final String ALL_BOOKS = "//book:book";
  4.     public static final String BOOK_BY_ID = "//book:book[@id='%s']";
  5.     public static final String BOOK_TITLE = "book:title/text()";
  6.     public static final String BOOK_AUTHOR = "auth:author/text()";
  7.     public static final String BOOK_PRICE = "book:price/text()";
  8. }
  9. // 使用常量
  10. XPathExpression expr = xpath.compile(BookXPathExpressions.ALL_BOOKS);
  11. NodeList books = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
复制代码

1. 创建专门的查询方法:为常用的查询操作创建专门的方法,提高代码的可读性和可维护性。
  1. // Java示例:创建专门的查询方法
  2. public class BookRepository {
  3.     private final XPath xpath;
  4.     private final NamespaceContext nsContext;
  5.    
  6.     public BookRepository(XPath xpath, NamespaceContext nsContext) {
  7.         this.xpath = xpath;
  8.         this.nsContext = nsContext;
  9.         this.xpath.setNamespaceContext(nsContext);
  10.     }
  11.    
  12.     public List<Book> findAllBooks(Document doc) throws Exception {
  13.         XPathExpression expr = xpath.compile("//book:book");
  14.         NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
  15.         
  16.         List<Book> books = new ArrayList<>();
  17.         for (int i = 0; i < nodes.getLength(); i++) {
  18.             books.add(extractBook(nodes.item(i)));
  19.         }
  20.         
  21.         return books;
  22.     }
  23.    
  24.     public Book findBookById(Document doc, String id) throws Exception {
  25.         XPathExpression expr = xpath.compile(String.format("//book:book[@id='%s']", id));
  26.         Node node = (Node) expr.evaluate(doc, XPathConstants.NODE);
  27.         
  28.         return node != null ? extractBook(node) : null;
  29.     }
  30.    
  31.     private Book extractBook(Node node) throws Exception {
  32.         Book book = new Book();
  33.         
  34.         // 提取ID
  35.         Element elem = (Element) node;
  36.         book.setId(elem.getAttribute("id"));
  37.         
  38.         // 提取标题
  39.         XPathExpression expr = xpath.compile("book:title/text()");
  40.         String title = (String) expr.evaluate(node, XPathConstants.STRING);
  41.         book.setTitle(title);
  42.         
  43.         // 提取作者
  44.         expr = xpath.compile("auth:author/text()");
  45.         String author = (String) expr.evaluate(node, XPathConstants.STRING);
  46.         book.setAuthor(author);
  47.         
  48.         // 提取价格
  49.         expr = xpath.compile("book:price/text()");
  50.         String priceStr = (String) expr.evaluate(node, XPathConstants.STRING);
  51.         book.setPrice(Double.parseDouble(priceStr));
  52.         
  53.         return book;
  54.     }
  55. }
  56. // 使用专门的查询方法
  57. BookRepository repo = new BookRepository(xpath, nsContext);
  58. List<Book> books = repo.findAllBooks(doc);
  59. Book book = repo.findBookById(doc, "1001");
复制代码

错误处理和调试技巧

处理XML和XPath时,良好的错误处理和调试技巧可以帮助快速定位和解决问题。

1. 验证XML文档:在处理XML文档之前,先验证其格式是否正确。
  1. // Java示例:验证XML文档
  2. public class XmlValidator {
  3.     public static boolean isValid(String xml) {
  4.         try {
  5.             DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  6.             factory.setNamespaceAware(true);
  7.             DocumentBuilder builder = factory.newDocumentBuilder();
  8.             builder.parse(new InputSource(new StringReader(xml)));
  9.             return true;
  10.         } catch (Exception e) {
  11.             e.printStackTrace();
  12.             return false;
  13.         }
  14.     }
  15. }
  16. // 使用验证器
  17. if (XmlValidator.isValid(xml)) {
  18.     // 处理XML
  19. } else {
  20.     System.err.println("Invalid XML document");
  21. }
复制代码

1. 记录XPath表达式和结果:在调试时,记录执行的XPath表达式和结果,便于分析问题。
  1. // Java示例:记录XPath表达式和结果
  2. public class XPathLogger {
  3.     private static final Logger logger = Logger.getLogger(XPathLogger.class.getName());
  4.    
  5.     public static Object evaluate(XPath xpath, String expression, Object item, QName returnType) throws Exception {
  6.         logger.info("Evaluating XPath expression: " + expression);
  7.         
  8.         XPathExpression expr = xpath.compile(expression);
  9.         Object result = expr.evaluate(item, returnType);
  10.         
  11.         logger.info("XPath result: " + result);
  12.         
  13.         return result;
  14.     }
  15. }
  16. // 使用日志记录器
  17. NodeList nodes = (NodeList) XPathLogger.evaluate(xpath, "//book:book", doc, XPathConstants.NODESET);
复制代码

1. 使用命名空间感知的XML查看器:使用支持命名空间的XML查看器(如XMLSpy、Oxygen XML Editor等)来检查XML文档和测试XPath表达式。
2. 分解复杂的XPath表达式:如果复杂的XPath表达式出现问题,可以将其分解为多个简单的表达式,逐步调试。

使用命名空间感知的XML查看器:使用支持命名空间的XML查看器(如XMLSpy、Oxygen XML Editor等)来检查XML文档和测试XPath表达式。

分解复杂的XPath表达式:如果复杂的XPath表达式出现问题,可以将其分解为多个简单的表达式,逐步调试。
  1. // Java示例:分解复杂的XPath表达式
  2. // 复杂的表达式
  3. String complexExpr = "//book:book[book:price > 20 and auth:author='John Doe']";
  4. // 分解为简单的表达式
  5. String allBooksExpr = "//book:book";
  6. XPathExpression expr = xpath.compile(allBooksExpr);
  7. NodeList books = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
  8. for (int i = 0; i < books.getLength(); i++) {
  9.     Node book = books.item(i);
  10.    
  11.     // 检查价格
  12.     expr = xpath.compile("book:price/text()");
  13.     String priceStr = (String) expr.evaluate(book, XPathConstants.STRING);
  14.     double price = Double.parseDouble(priceStr);
  15.    
  16.     // 检查作者
  17.     expr = xpath.compile("auth:author/text()");
  18.     String author = (String) expr.evaluate(book, XPathConstants.STRING);
  19.    
  20.     if (price > 20 && "John Doe".equals(author)) {
  21.         // 处理符合条件的书籍
  22.     }
  23. }
复制代码

性能优化策略

在处理大型XML文档或执行大量XPath查询时,性能优化尤为重要。

1. 使用索引:如果频繁查询特定元素,可以考虑创建索引来加速查询。
  1. // Java示例:使用Map索引元素
  2. public class BookIndex {
  3.     private final Map<String, Element> bookById = new HashMap<>();
  4.    
  5.     public void indexBooks(Document doc) throws Exception {
  6.         XPathFactory xPathFactory = XPathFactory.newInstance();
  7.         XPath xpath = xPathFactory.newXPath();
  8.         
  9.         // 设置命名空间上下文
  10.         NamespaceContext ctx = new BookNamespaceContext();
  11.         xpath.setNamespaceContext(ctx);
  12.         
  13.         // 获取所有书籍
  14.         XPathExpression expr = xpath.compile("//book:book");
  15.         NodeList books = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
  16.         
  17.         // 创建索引
  18.         for (int i = 0; i < books.getLength(); i++) {
  19.             Element book = (Element) books.item(i);
  20.             String id = book.getAttribute("id");
  21.             bookById.put(id, book);
  22.         }
  23.     }
  24.    
  25.     public Element getBookById(String id) {
  26.         return bookById.get(id);
  27.     }
  28. }
  29. // 使用索引
  30. BookIndex index = new BookIndex();
  31. index.indexBooks(doc);
  32. Element book = index.getBookById("1001");
复制代码

1. 批量处理:如果需要对多个元素执行相同的操作,考虑批量处理以减少解析开销。
  1. // Java示例:批量处理
  2. public class BookBatchProcessor {
  3.     public void processBooks(Document doc, Consumer<Element> processor) throws Exception {
  4.         XPathFactory xPathFactory = XPathFactory.newInstance();
  5.         XPath xpath = xPathFactory.newXPath();
  6.         
  7.         // 设置命名空间上下文
  8.         NamespaceContext ctx = new BookNamespaceContext();
  9.         xpath.setNamespaceContext(ctx);
  10.         
  11.         // 获取所有书籍
  12.         XPathExpression expr = xpath.compile("//book:book");
  13.         NodeList books = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
  14.         
  15.         // 批量处理
  16.         for (int i = 0; i < books.getLength(); i++) {
  17.             processor.accept((Element) books.item(i));
  18.         }
  19.     }
  20. }
  21. // 使用批量处理器
  22. BookBatchProcessor batchProcessor = new BookBatchProcessor();
  23. batchProcessor.processBooks(doc, book -> {
  24.     // 处理每个书籍元素
  25.     String id = book.getAttribute("id");
  26.     System.out.println("Processing book: " + id);
  27. });
复制代码

1. 使用适当的集合类型:根据查询需求选择合适的集合类型,如使用HashSet进行快速查找,使用ArrayList进行顺序访问。
  1. // Java示例:使用适当的集合类型
  2. public class BookCollection {
  3.     private final List<Element> books = new ArrayList<>();
  4.     private final Map<String, Element> bookById = new HashMap<>();
  5.     private final Map<String, List<Element>> booksByAuthor = new HashMap<>();
  6.    
  7.     public void addBook(Element book) {
  8.         String id = book.getAttribute("id");
  9.         books.add(book);
  10.         bookById.put(id, book);
  11.         
  12.         // 按作者索引
  13.         try {
  14.             XPathFactory xPathFactory = XPathFactory.newInstance();
  15.             XPath xpath = xPathFactory.newXPath();
  16.             
  17.             // 设置命名空间上下文
  18.             NamespaceContext ctx = new BookNamespaceContext();
  19.             xpath.setNamespaceContext(ctx);
  20.             
  21.             // 获取作者
  22.             XPathExpression expr = xpath.compile("auth:author/text()");
  23.             String author = (String) expr.evaluate(book, XPathConstants.STRING);
  24.             
  25.             // 添加到作者索引
  26.             booksByAuthor.computeIfAbsent(author, k -> new ArrayList<>()).add(book);
  27.         } catch (Exception e) {
  28.             e.printStackTrace();
  29.         }
  30.     }
  31.    
  32.     public List<Element> getAllBooks() {
  33.         return new ArrayList<>(books);
  34.     }
  35.    
  36.     public Element getBookById(String id) {
  37.         return bookById.get(id);
  38.     }
  39.    
  40.     public List<Element> getBooksByAuthor(String author) {
  41.         return booksByAuthor.getOrDefault(author, Collections.emptyList());
  42.     }
  43. }
  44. // 使用适当的集合类型
  45. BookCollection collection = new BookCollection();
  46. // 添加书籍
  47. collection.addBook(book1);
  48. collection.addBook(book2);
  49. // 查询
  50. List<Element> allBooks = collection.getAllBooks();
  51. Element book = collection.getBookById("1001");
  52. List<Element> booksByAuthor = collection.getBooksByAuthor("John Doe");
复制代码

总结

XPath和XML命名空间是XML处理中的两个重要概念,正确理解和处理它们对于高效、准确地处理XML文档至关重要。本指南从基础概念出发,逐步深入到高级技巧,全面介绍了XPath中处理XML命名空间的方法和策略。

我们首先了解了XML命名空间的基本概念,包括命名空间的声明语法、默认命名空间和命名空间的作用域。然后,我们介绍了XPath的基础知识,包括路径表达式、轴、节点测试、谓语以及函数和运算符。

接着,我们详细讨论了XPath中常见的命名空间问题,包括为什么简单的XPath表达式无法匹配带有命名空间的元素、默认命名空间的影响以及命名空间冲突问题。针对这些问题,我们提供了一系列解决方案,从基本的方法如使用命名空间前缀和处理默认命名空间,到高级技巧如处理动态命名空间、处理嵌套命名空间以及忽略命名空间的技巧。

我们还展示了在不同编程语言中实现XPath命名空间处理的方法,包括Java、Python、C#和JavaScript,为不同背景的开发者提供了实用的参考。

在实际应用场景部分,我们探讨了Web服务响应处理、配置文件解析、数据转换以及大型XML文档处理等常见场景,并提供了详细的示例代码。

最后,我们分享了一系列最佳实践和性能优化策略,包括命名空间处理的性能考虑、代码组织和可维护性、错误处理和调试技巧以及性能优化策略,帮助开发者编写更高效、更可维护的代码。

通过掌握本指南中介绍的知识和技巧,开发者将能够更加自信地处理带有命名空间的XML文档,编写更高效、更准确的XPath查询,从而提升开发效率,减少调试时间,提高代码质量。

XPath和XML命名空间处理是XML开发中的核心技能,希望本指南能够帮助开发者从基础到高级全面掌握这一技能,在实际项目中游刃有余地处理各种XML命名空间问题。
「七転び八起き(ななころびやおき)」
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则