会计考友 发表于 2012-8-4 12:44:44

Java认证辅导之关于Java语言的XPathAPI(5)

Java认证辅导之关于Java语言的XPathAPI(5)
名称空间上下文
若 XML 文档中的元素在名称空间中,查询该文档的 XPath 表达式必须使用相同的名称空间。XPath 表达式不一定要使用相同的前缀,只需要名称空间 URI 相同即可。事实上,如果 XML 文档使用默认名称空间,那么尽管目标文档没有使用前缀,XPath 表达式也必须使用前缀。
但是,Java 程序不是 XML 文档,因此不能用一般的名称空间解析。必须提供一个对象将前缀映射到名称空间 URI。该对象是javax.xml.namespace.NamespaceContext 接口的实例。比如,假设图书文档放在 http://www.example.com/books 名称空间中,如 清单 5 所示:
清单 5. 使用默认名称空间的 XML 文档

            Snow Crash         Neal Stephenson         Spectra         0553380958         14.95            查找 Neal Stephenson 全部著作标题的 XPath 表达式就要改为 //pre:book/pre:title/text()。但是,必须将前缀 pre 映射到 URI http://www.example.com/books。NamespaceContext 接口在 Java 软件开发工具箱(JDK)或 JAXP 中没有默认实现似乎有点笨,但确实如此。不过,自己实现也不难。清单 6 对一个名称空间给出了简单的实现。还需要映射xml 前缀。
清单 6. 绑定一个名称空间和默认名称空间的简单上下文
import java.util.Iterator; import javax.xml.*; import javax.xml.namespace.NamespaceContext; public class PersonalNamespaceContext implements NamespaceContext {   public String getNamespaceURI(String prefix) {         if (prefix == null) throw new NullPointerException("Null prefix");         else if ("pre".equals(prefix)) return "http://www.example.org/books";         else if ("xml".equals(prefix)) return XMLConstants.XML_NS_URI;         return XMLConstants.NULL_NS_URI;   }   // This method isn't necessary for XPath processing.   public String getPrefix(String uri) {         throw new UnsupportedOperationException();   }   // This method isn't necessary for XPath processing either.   public Iterator getPrefixes(String uri) {         throw new UnsupportedOperationException();   } } 用映射存储绑定和增加 setter 方法实现名称空间上下文的重用也不难。创建 NamespaceContext 对象后,在编译表达式之前将其安装到 XPath 对象上。以后就可以像以前一样是用这些前缀查询了。比如:
清单 7. 使用名称空间的 XPath 查询

XPathFactory factory = XPathFactory.newInstance(); XPath xpath = factory.newXPath(); xpath.setNamespaceContext(new PersonalNamespaceContext()); XPathExpression expr   = xpath.compile("//pre:book/pre:title/text()");Object result = expr.evaluate(doc, XPathConstants.NODESET); NodeList nodes = (NodeList) result; for (int i = 0; i < nodes.getLength(); i++) {   System.out.println(nodes.item(i).getNodeValue()); } 函数求解器
有时候,在 Java 语言中定义用于 XPath 表达式的扩展函数很有用。这些函数可以执行用纯 XPath 很难或者无法执行的任务。不过必须是真正的函数,而不是随意的方法。就是说不能有副作用。(XPath 函数可以按照任意的顺序求值任意多次。)
通过 Java XPath API 访问的扩展函数必须实现 javax.xml.xpath.XPathFunction 接口。这个接口只声明了一个方法 evaluate:

public Object evaluate(List args) throws XPathFunctionException 该方法必须返回 Java 语言能够转换到 XPath 的五种类型之一:
◆ String
◆ Double
◆ Boolean
◆ Nodelist
◆ Node
比如,清单 8 显示了一个扩展函数,它检查 ISBN 的校验和并返回 Boolean。这个校验和的基本规则是前九位数的每一位乘上它的位置(即第一位数乘上 1,第二位数乘上 2,依次类推)。将这些数加起来然后取除以 11 的余数。如果余数是 10,那么最后一位数就是 X。
清单 8. 检查 ISBN 的 XPath 扩展函数

import java.util.List; import javax.xml.xpath.*; import org.w3c.dom.*; public class ISBNValidator implements XPathFunction {   // This class could easily be implemented as a Singleton.   public Object evaluate(List args) throws XPathFunctionException {   if (args.size() != 1) {       throw new XPathFunctionException("Wrong number of arguments to valid-isbn()");   }   String isbn;   Object o = args.get(0);   // perform conversions   if (o instanceof String) isbn = (String) args.get(0);   else if (o instanceof Boolean) isbn = o.toString();   else if (o instanceof Double) isbn = o.toString();   else if (o instanceof NodeList) {         NodeList list = (NodeList) o;         Node node = list.item(0);         // getTextContent is available in Java 5 and DOM 3.         // In Java 1.4 and DOM 2, you'd need to recursively         // accumulate the content.         isbn= node.getTextContent();   }   else {         throw new XPathFunctionException("Could not convert argument type");   }   char[] data = isbn.toCharArray();   if (data.length != 10) return Boolean.FALSE;   int checksum = 0;   for (int i = 0; i < 9; i++) {         checksum += (i+1) * (data-'0');   }   int checkdigit = checksum % 11;   if (checkdigit + '0' == data || (data == 'X' && checkdigit == 10)) {         return Boolean.TRUE;   }   return Boolean.FALSE;   } } 下一步让这个扩展函数能够在 Java 程序中使用。为此,需要在编译表达式之前向 XPath 对象安装javax.xml.xpath.XPathFunctionResolver。函数求解器将函数的 XPath 名称和名称空间 URI 映射到实现该函数的 Java 类。清单 9是一个简单的函数求解器,将扩展函数 valid-isbn 和名称空间 http://www.example.org/books 映射到 清单 8 中的类。比如,XPath 表达式 //book 可以找到 ISBN 校验和不匹配的所有图书。
清单 9. 识别 valid-isbn 扩展函数的上下文

import javax.xml.namespace.QName; import javax.xml.xpath.*; public class ISBNFunctionContext implements XPathFunctionResolver {   private static final QName name    = new QName("http://www.example.org/books", "valid-isbn");   public XPathFunction resolveFunction(QName name, int arity) {       if (name.equals(ISBNFunctionContext.name) && arity == 1) {         return new ISBNValidator();       }       return null;   } } 由于扩展函数必须有名称空间,所以计算包含扩展函数的表达式时必须使用 NamespaceResolver,即便查询的文档没有使用任何名称空间。由于 XPathFunctionResolver、XPathFunction 和 NamespaceResolver 都是接口,如果方便的话可以将它们放在所有的类中。
页: [1]
查看完整版本: Java认证辅导之关于Java语言的XPathAPI(5)