MyBatis源码学习之XPathParser及XNode
简介
MyBatis使用XPath技术解析XML文档,MyBatis提供的XPathParser类对XPath的进行了封装,提供了更为友好的方法以方便用户使用。
XPathParser内部成员
XPathParser是一个直接可以使用的类,主要持有XPath、Document及Properties对象的引用。如下所示:
public class XPathParser {
// 要解析的XML文档
private final Document document;
private boolean validation;
private EntityResolver entityResolver;
// 属性对象
private Properties variables;
// 用来解析XML文档的XPath对象
private XPath xpath;
}
XPathParser对象创建
XPathParser提供了多个重载的构造方法以便客户创建XPathParser对象,构造方法中调用了commonConstructor方法,该方法使用Java标准技术创建了XPath对象,调用createDocument方法,该方法使用Java标准技术创建了Document对象。下面是commonConstructor方法:
private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
this.validation = validation;
this.entityResolver = entityResolver;
this.variables = variables;
// 这里使用Java标准技术创建了XPath对象
XPathFactory factory = XPathFactory.newInstance();
this.xpath = factory.newXPath();
}
XPathParser主要方法
XPathParser提供了多个重载的evalXxx方法以方便客户解析XML文档,Xxx是某个具体的类型,如Integer、String、XNode等等,evalXxx方法的实现都基于evaluate方法,这个方法使用XPath解析XML文档,并获得解析出的对象。
// 内部使用这个方法解析
// expression: 符合XPath语法的路径
// root:要解析的XML文档的根节点
// returnType:解析结果的返回类型
private Object evaluate(String expression, Object root, QName returnType) {
try {
// 调用XPath对象的evaluate
return xpath.evaluate(expression, root, returnType);
} catch (Exception e) {
throw new BuilderException("Error evaluating XPath. Cause: " + e, e);
}
}
evalXxx方法在evaluate方法的基础上作了一些处理,其中一个最重要的处理是将XPath解析获得的Node对象包装为XNode对象,我们可以看一下evalNode及evalNodes代码:
// 将XPath解析出的Node对象,转换为XNode返回
public XNode evalNode(Object root, String expression) {
Node node = (Node) evaluate(expression, root, XPathConstants.NODE);
if (node == null) {
return null;
}
// 基于Node对象创建XNode对象,并返回
return new XNode(this, node, variables);
}
// 将XPath对象转换出的Node对象的集合,转换为XNode对象的集合返回
public List<XNode> evalNodes(Object root, String expression) {
List<XNode> xnodes = new ArrayList<>();
// 将NodeList中的每个Node转换为XNode对象,加到List中返回
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;
}
evalInteger、evalLong这些方法或者就是XPath解析出的结果,或者是将XPath解析出的结果作类型转换。详情可看源代码。但我们要特别关注一下evalString方法。
public String evalString(Object root, String expression) {
String result = (String) evaluate(expression, root, XPathConstants.STRING);
// 处理字符串result中有无${name},有的话则到variables查找name对应的值,并返回新的字符串
result = PropertyParser.parse(result, variables);
return result;
}
注意这个方法调用了PropertyParser.parse方法对解析出的字符串作了进一步处理,即外理字符串中的${name},能在属性对象variables中查找有无name,若有则用其值替换${name},即PropertyParser.parse方法对${}进行处理,处理细节见PropertyParser源代码分析。
XNode是MyBatis对Node的包装,Node是w3c标准DOM中定义的节点,XNode的重点是能处理属性及文本中的${}占位符,它的内部成员有:
public class XNode {
private final Node node; //所包装的Node对象
private final String name;
private final String body;
private final Properties attributes; // 节点的属性对象
private final Properties variables; //外部传入的属性对象
private final XPathParser xpathParser;
}
它的构造方法中,处理了节点所有属性的值,特别是处理了${}占位符,同时处理了文本中的的${}
// 构造方法
public XNode(XPathParser xpathParser, Node node, Properties variables) {
this.xpathParser = xpathParser;
this.node = node;
this.name = node.getNodeName();
this.variables = variables;
// 处理了属性中的${}
this.attributes = parseAttributes(node);
// 处理了文本中的${},处理时都是使用PropertyParser.parse方法
this.body = parseBody(node);
}
测试
测试的XML文档是:
<?xml version="1.0" encoding="utf-8" ?>
<persons id="100" value="100">
<person id="11234" lang="en">
<name type="1">tom</name>
<age>10</age>
</person>
<person id="3" lang="ch">
<name type="3">zhang3</name>
<age>30</age>
</person>
<dog>
<name heigh="20">${username}</name>
<age>30</age>
</dog>
</persons>
代码是:
public class XPathParserDemo1 {
public static void main(String[] args) throws IOException, ParserConfigurationException, SAXException {
// 要解析的XML文档
InputStream inputStream = XPathParserDemo1.class.getResourceAsStream("/xml/persons.xml");
// 模拟属性文件
Properties properties = new Properties();
properties.setProperty("username","tom");
// 创建XPathParser对象
XPathParser xPathParser = new XPathParser(inputStream,false,properties);
// 查找第一个person元素的id属性值,
String expression = "/persons/person[1]//@id";
// 直接获得Interger值
Integer id = xPathParser.evalInteger(expression);
System.out.println("id="+id);
// 查找dog元素下的子元素name节点,获得XNode对象
expression = "/persons/dog/name";
XNode xNode = xPathParser.evalNode(expression);
// 获得节点体中的文本,注意这里处理了${username},得到是username对应的值tom
String str = xNode.getStringBody();
System.out.println(str); // 输出tom
}
}

浙公网安备 33010602011771号