Java 5 推出了 javax.xml.xpath 包,这是一个用于 XPath 文档查询的独立于 XML 对象模型的库。
强大的xpath表达式支持对xml document文档检索信息。
Snow Crash Neal Stephenson Spectra 0553380958 14.95 Burning Tower Larry Niven Jerry Pournelle 0743416910 5.99 Zodiac Neal Stephenson Spectra 0553573862 7.50
import java.io.IOException; import org.w3c.dom.*; import org.xml.sax.SAXException; import javax.xml.parsers.*; import javax.xml.xpath.*; public class XPathExample { public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { //创建 XPathFactory: DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); domFactory.setNamespaceAware(true); // never forget this! DocumentBuilder builder = domFactory.newDocumentBuilder(); Document doc = builder.parse("book.xml"); XPathFactory factory = XPathFactory.newInstance(); //这个工厂创建 XPath 对象: XPath xpath = factory.newXPath(); //设置命名空间 // xpath.setNamespaceContext(new PersonalNamespaceContext()); //XPath 对象编译 XPath 表达式: XPathExpression expr = xpath.compile("//book[author='Neal Stephenson']/title/text()"); //doc是整个节点 /** * XPathConstants.NODESET XPathConstants.BOOLEAN XPathConstants.NUMBER XPathConstants.STRING XPathConstants.NODE 定义了返回类型 xpath 和java的映射关系 number 映射为 java.lang.Double string 映射为 java.lang.String boolean 映射为 java.lang.Boolean node-set 映射为 org.w3c.dom.NodeList */ 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()); } } }
xpath 表达式代码
XPathExpression expr = xpath.compile("//book[author='Neal Stephenson']/title/text()");
//book[author='Neal Stephenson'book标签 下的text为Neal Stephenson的author标签
/title/test()是title 标签的文本
很简单吧,再说一下
实体解析器SAX EntityResolver
这里有它详细的解释:http://www.ibm.com/developerworks/cn/xml/tips/x-tipent/
让xml文档包含外部实体引用
package com.ibm.developerWorks; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.xml.sax.SAXException; public class CopyrightResolver implements EntityResolver { public InputSource resolveEntity(String publicID, String systemID) throws SAXException { if (systemID.equals("http://www.ibm.com/developerworks/copyright.xml")) { // Return local copy of the copyright.xml file return new InputSource("/usr/local/content/localCopyright.xml"); } // If no match, returning null makes process continue normally return null; }
再你的xml解析类中每次解析都会调用这个方法,这样就避免了每次都要加载外部资源的消耗。
xpase的公用类:
import java.io.InputStream; import java.io.Reader; import java.io.StringReader; import java.util.ArrayList; import java.util.List; import java.util.Properties; import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFactory; import org.apache.ibatis.builder.BuilderException; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.EntityResolver; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; public class XPathParser { private Document document;//dom对象 private boolean validation;//是否验证 domFactory.setNamespaceAware(validation); private EntityResolver entityResolver;//实体解析器 private Properties variables;//配置文件 private XPath xpath;//xpath public XPathParser(String xml) { commonConstructor(false, null, null);//初始化XPath对象 this.document = createDocument(new InputSource(new StringReader(xml)));//获取dom对象 } public XPathParser(Reader reader) { commonConstructor(false, null, null); this.document = createDocument(new InputSource(reader)); } public XPathParser(InputStream inputStream) { commonConstructor(false, null, null); this.document = createDocument(new InputSource(inputStream)); } public XPathParser(Document document) { commonConstructor(false, null, null); this.document = document; } public XPathParser(String xml, boolean validation) { commonConstructor(validation, null, null); this.document = createDocument(new InputSource(new StringReader(xml))); } public XPathParser(Reader reader, boolean validation) { commonConstructor(validation, null, null); this.document = createDocument(new InputSource(reader)); } public XPathParser(InputStream inputStream, boolean validation) { commonConstructor(validation, null, null); this.document = createDocument(new InputSource(inputStream)); } public XPathParser(Document document, boolean validation) { commonConstructor(validation, null, null); this.document = document; } public XPathParser(String xml, boolean validation, Properties variables) { commonConstructor(validation, variables, null); this.document = createDocument(new InputSource(new StringReader(xml))); } public XPathParser(Reader reader, boolean validation, Properties variables) { commonConstructor(validation, variables, null); this.document = createDocument(new InputSource(reader)); } public XPathParser(InputStream inputStream, boolean validation, Properties variables) { commonConstructor(validation, variables, null); this.document = createDocument(new InputSource(inputStream)); } public XPathParser(Document document, boolean validation, Properties variables) { commonConstructor(validation, variables, null); this.document = document; } public XPathParser(String xml, boolean validation, Properties variables, EntityResolver entityResolver) { commonConstructor(validation, variables, entityResolver); this.document = createDocument(new InputSource(new StringReader(xml))); } public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) { commonConstructor(validation, variables, entityResolver); this.document = createDocument(new InputSource(reader)); } public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) { commonConstructor(validation, variables, entityResolver); this.document = createDocument(new InputSource(inputStream)); } public XPathParser(Document document, boolean validation, Properties variables, EntityResolver entityResolver) { commonConstructor(validation, variables, entityResolver); this.document = document; } public void setVariables(Properties variables) { this.variables = variables; } public String evalString(String expression) { return evalString(document, expression); } public String evalString(Object root, String expression) { String result = (String) evaluate(expression, root, XPathConstants.STRING); result = PropertyParser.parse(result, variables); return result; } public Boolean evalBoolean(String expression) { return evalBoolean(document, expression); } public Boolean evalBoolean(Object root, String expression) { return (Boolean) evaluate(expression, root, XPathConstants.BOOLEAN); } public Short evalShort(String expression) { return evalShort(document, expression); } public Short evalShort(Object root, String expression) { return Short.valueOf(evalString(root, expression)); } public Integer evalInteger(String expression) { return evalInteger(document, expression); } public Integer evalInteger(Object root, String expression) { return Integer.valueOf(evalString(root, expression)); } public Long evalLong(String expression) { return evalLong(document, expression); } public Long evalLong(Object root, String expression) { return Long.valueOf(evalString(root, expression)); } public Float evalFloat(String expression) { return evalFloat(document, expression); } public Float evalFloat(Object root, String expression) { return Float.valueOf(evalString(root, expression)); } public Double evalDouble(String expression) { return evalDouble(document, expression); } public Double evalDouble(Object root, String expression) { return (Double) evaluate(expression, root, XPathConstants.NUMBER); } public ListevalNodes(String expression) { return evalNodes(document, expression); } public List evalNodes(Object root, String expression) { List xnodes = new ArrayList (); NodeList nodes = (NodeList) evaluate(expression, root, XPathConstants.NODESET); for (int i = 0; i < nodes.getLength(); i++) { xnodes.add(new XNode(this, nodes.item(i), variables)); } return xnodes; } public XNode evalNode(String expression) { return evalNode(document, expression); } public XNode evalNode(Object root, String expression) { Node node = (Node) evaluate(expression, root, XPathConstants.NODE); if (node == null) { return null; } return new XNode(this, node, variables); } private Object evaluate(String expression, Object root, QName returnType) { try { return xpath.evaluate(expression, root, returnType); } catch (Exception e) { throw new BuilderException("Error evaluating XPath. Cause: " + e, e); } } private Document createDocument(InputSource inputSource) { // important: this must only be called AFTER common constructor try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(validation); factory.setNamespaceAware(false); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(false); factory.setCoalescing(false); factory.setExpandEntityReferences(true); DocumentBuilder builder = factory.newDocumentBuilder(); builder.setEntityResolver(entityResolver);//设置实体解析器 builder.setErrorHandler(new ErrorHandler() { public void error(SAXParseException exception) throws SAXException { throw exception; } public void fatalError(SAXParseException exception) throws SAXException { throw exception; } public void warning(SAXParseException exception) throws SAXException { } }); return builder.parse(inputSource); } catch (Exception e) { throw new BuilderException("Error creating document instance. Cause: " + e, e); } } private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) { this.validation = validation; this.entityResolver = entityResolver; this.variables = variables; XPathFactory factory = XPathFactory.newInstance(); this.xpath = factory.newXPath(); } }
这里用到了一个字符串解析器:
PropertyParser 它是来处理字符串中${}这样的字符,去配置文件中查找。
解析器模式:
import java.util.Properties; public class PropertyParser { public static String parse(String string, Properties variables) { VariableTokenHandler handler = new VariableTokenHandler(variables); GenericTokenParser parser = new GenericTokenParser("${", "}", handler); return parser.parse(string); } private static class VariableTokenHandler implements TokenHandler { private Properties variables; public VariableTokenHandler(Properties variables) { this.variables = variables; } public String handleToken(String content) { if (variables != null && variables.containsKey(content)) { return variables.getProperty(content); } return "${" + content + "}"; } } }
GenericTokenParser.java
public class GenericTokenParser { private final String openToken; private final String closeToken; private final TokenHandler handler; public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) { this.openToken = openToken; this.closeToken = closeToken; this.handler = handler; } public String parse(String text) { StringBuilder builder = new StringBuilder(); if (text != null && text.length() > 0) {//如果传入的字符串有值 //将字符串转为字符数组 char[] src = text.toCharArray(); int offset = 0; //判断openToken在text中的位置,注意indexOf函数的返回值-1表示不存在,0表示在在开头的位置 int start = text.indexOf(openToken, offset); while (start > -1) { if (start > 0 && src[start - 1] == '\\') { //如果text中在openToken前存在转义符就将转义符去掉。如果openToken前存在转义符,start的值必然大于0,最小也为1 //因为此时openToken是不需要进行处理的,所以也不需要处理endToken。接着查找下一个openToken builder.append(src, offset, start - 1).append(openToken); offset = start + openToken.length();//重设offset } else { int end = text.indexOf(closeToken, start); if (end == -1) {//如果不存在openToken,则直接将offset位置后的字符添加到builder中 builder.append(src, offset, src.length - offset); offset = src.length;//重设offset } else { builder.append(src, offset, start - offset);//添加openToken前offset后位置的字符到bulider中 offset = start + openToken.length();//重设offset String content = new String(src, offset, end - offset);//获取openToken和endToken位置间的字符串 builder.append(handler.handleToken(content));//调用handler进行处理 offset = end + closeToken.length();//重设offset } } start = text.indexOf(openToken, offset);//开始下一个循环 } //只有当text中不存在openToken且text.length大于0时才会执行下面的语句 if (offset < src.length) { builder.append(src, offset, src.length - offset); } } return builder.toString(); } }
简单的说,这个函数的作用就是将openToken和endToken间的字符串取出来用handler处理下,然后再拼接到一块。我们接下来看一个具体的handler,了解下它对传入的字符串做了怎样的处理。
在XMLConfigBuilder这个类中有它的调用实例:
//很关键是xpath的表达式语言 匹配对象 放入configuration中 private void parseConfiguration(XNode root) { try { propertiesElement(root.evalNode("properties")); //issue #117 read properties first typeAliasesElement(root.evalNode("typeAliases")); pluginElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); settingsElement(root.evalNode("settings")); environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631 databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlerElement(root.evalNode("typeHandlers")); mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
这里使用了解析器设计模式,这里的文件可以单独作为我们以后对xml文件解析的使用。