【发布时间】:2014-11-18 14:18:45
【问题描述】:
在 Java 中,如何使用 SAX Parser 评估 XML 上的 XPATH 表达式?
需要更动态的方式,因为 XML 格式不固定。所以我应该能够通过以下
- xpath 作为字符串
- xml 作为字符串/输入源
类似于 Utility.evaluate("/test/@id='123'", "")
【问题讨论】:
标签: java xml parsing xpath sax
在 Java 中,如何使用 SAX Parser 评估 XML 上的 XPATH 表达式?
需要更动态的方式,因为 XML 格式不固定。所以我应该能够通过以下
类似于 Utility.evaluate("/test/@id='123'", "")
【问题讨论】:
标签: java xml parsing xpath sax
这是一个例子:
//First create a Document
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new File("test.xml"));
//Init the xpath factory
XPath xPath = XPathFactory.newInstance().newXPath();
String expression = "/company/employee";
//read a nodelist using xpath
NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(doc, XPathConstants.NODESET);
编辑:
如果你想使用 SAX 解析器,那么你不能使用 Java 的 XPath 对象,见https://docs.oracle.com/javase/7/docs/api/javax/xml/xpath/package-summary.html
XPath 语言为从 XML 文档中选择节点提供了一种简单、简洁的语法。 XPath 还提供了将 XML 文档对象模型 (DOM) 树中的节点转换为布尔值、双精度值或字符串值的规则。 XPath 是 W3C 定义的语言和官方 W3C 推荐; W3C 承载 XML 路径语言 (XPath) 版本 1.0 规范。
XPath 始于 1999 年,作为 XSLT 和 XPointer 语言的补充,但最近作为一种独立语言变得流行,因为单个 XPath 表达式可用于替换多行 DOM API 代码。
如果您想使用 SAX,可以查看此问题中详细说明的库:Is there any XPath processor for SAX model?。
虽然 XPath 的机制并不真正适合 SAX。实际上,使用 SAX 解析器不会在内存中创建 XML 树。因此,您不能有效地使用 XPath,因为它不会看到未加载的节点。
【讨论】:
只有一小部分 XPath 适合流式评估,即在解析输入文档时即时评估。因此,周围没有很多流式 XPath 处理器;其中大部分是学术研究项目的产物。
您可以尝试的一件事是 Saxon-EE 流式 XQuery。这是 XQuery 的一个小子集,它允许流式执行(它将允许像您的示例这样的表达式)。详情在
http://www.saxonica.com/documentation/#!sourcedocs/streaming/streamed-query
【讨论】:
Oracle 用于 Java 的 XQuery 处理器将“动态”流式处理路径表达式: https://docs.oracle.com/database/121/ADXDK/adx_j_xqj.htm#ADXDK99930
具体来说,这里有关于流式传输的信息,包括一个示例: https://docs.oracle.com/database/121/ADXDK/adx_j_xqj.htm#ADXDK119
但它不会使用 SAX 进行流式传输。您必须将输入 XML 绑定为 StAX、InputStream 或 Reader 才能获得流式评估。
【讨论】:
您可以在使用 Saxon 的 XPath 中使用 SAXSource,但是 - 这很重要 - 请注意,底层实现几乎肯定仍会加载和缓冲内存中的部分或全部文档以评估 xpath。它可能不会是一个完整的 DOM 树(Saxon 依赖于自己的称为 TinyTree 的结构,它支持延迟加载和各种其他优化),因此它比使用大多数 DOM 实现要好,但它仍然涉及将文档加载到记忆。如果您担心大型数据集的内存负载,它可能对您没有多大帮助,您最好使用其他人建议的流式 xpath/xquery 选项之一。
实用程序方法的实现可能如下所示:
import java.io.StringReader;
import javax.xml.namespace.QName;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.sax.SAXSource;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import org.xml.sax.InputSource;
import net.sf.saxon.xpath.XPathFactoryImpl;
public class XPathUtils {
public static Object evaluate(String xpath, String xml, QName returnType)
throws Exception {
SAXParser parser = (SAXParser) SAXParserFactory.newInstance()
.newSAXParser();
InputSource source = new InputSource(new StringReader(xml));
SAXSource saxSource = new SAXSource(parser.getXMLReader(), source);
XPath xPath = new XPathFactoryImpl().newXPath();
return xPath.evaluate(xpath, saxSource, returnType);
}
public static String xpathString(String xpath, String xml)
throws Exception {
return (String) evaluate(xpath, xml, XPathConstants.STRING);
}
public static boolean xpathBool(String xpath, String xml) throws Exception {
return (Boolean) evaluate(xpath, xml, XPathConstants.BOOLEAN);
}
public static Number xpathNumber(String xpath, String xml) throws Exception {
return (Number) evaluate(xpath, xml, XPathConstants.NUMBER);
}
public static void main(String[] args) throws Exception {
System.out.println(xpathString("/root/@id", "<root id='12345'/>"));
}
}
这是因为 Saxon XPath 实现支持 SAXSource 作为 evaluate() 的上下文。请注意,使用内置的 Apaache XPath 实现尝试此操作将引发异常。
【讨论】: