· XML文件是一种可扩展标记语言。做后台开发接触到的XML文件一般是以配置文件的身份登场,虽说现在几乎不用我们自行解析XML配置文件,但是技多不压身,万一哪天我们需要自己开发框架了,这门技能就成为了必须。
· XML文件的格式非常简单,第一行是文件声明,后面就是我们自行添加的内容。书写XML文件有几点注意事项:
·示例如下:
<beans>
<bean id="studentService" class="com.memoforward.service.StudentService">
<property name="studentDao" ref="studentDao"/>
bean>
<bean id="studentDao" class="com.memoforward.dao.StudentDao"/>
<beanTest >beanTestValuebeanTest>
beans>
· 由上面的格式可知,其实XML与HTML相差并不大,我们访问和操作HTML文档使用的工具是HTML DOM 树。异曲同工,Dom4j也参照了这种方法,其使用SAXReader对象将整个XML文档读取,构建出了document树对象。通过这个树对象,配合上XPath工具,我们就可以很轻松地访问到整个文件的所有节点的数据了,其中节点(元素)就是Element对象,节点中的属性就是Atrribute对象。
· 所以,解析XML的步骤就是:创建SAXReader对象 – SAXReader获取XML文件的Document树对象 – document树获取Element元素对象 – Element元素对象获取其元素的属性Attribute对象。这一套流程下来,基本想读取什么值都可以。
· 本博客使用Dom4j+Xpath来解释XML文件。使用这种方式需要引入两个jar包,一个是dom4j包,另一个是Xpath通用引擎包jaxen。注:如果xml文件不大,推荐使用此种方式解析xml文件(因为使用简单方便);如果XML巨大,可以使用JDK自带的SAX解析器去“边读边写”提高效率,但因为其操作比较复杂,需要定义自己的处理器,本博客就不描述此种方法了。
其Maven依赖如下:
<dependency>
<groupId>dom4jgroupId>
<artifactId>dom4jartifactId>
<version>1.6.1version>
dependency>
<dependency>
<groupId>jaxengroupId>
<artifactId>jaxenartifactId>
<version>1.1.6version>
dependency>
· 在正式开始解析之前,不得不介绍一下Xpath是为何物。
· XPath 是一门在 XML 文档中查找信息的语言,用于在 XML 文档中对元素和属性进行遍历。XPath 使用路径表达式来选取 XML 文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。
· 由上述概念可知,我们解析XML文件的核心就在于:使用XPath的路径表达式在document树对象中检索节点。
· 树对象的获取在1.2节已经介绍了,非常简单,因此难点就在于路径表达式的语法了。
· Xpath是W3C的一个标准,因此W3School的在线教程网站上就有,传送门如下:W3CXpath语法
·最有用的路径表达式如下:
表达式 | 描述 | 使用(依据上图的示例) | 含义 |
---|---|---|---|
nodename | 选取此节点下所有的子节点 | beans | 表示beans元素下所有的子节点 |
/ | 表示从根节点开始选取 | /beans/bean | 表示从根节点开始找到所有beans下所有名为bean的子节点 |
// | 不管节点在什么位置,选取该节点 | //property | 表示选取property节点,不管它在什么位置 |
. | 选取当前节点 | / | / |
… | 选取当前节点的父节点 | / | / |
@ | 选取属性 | 一般用于谓语中 | / |
·谓语
· 谓语用来查找某个特定的节点或者包含某个指定的值的节点。谓语被嵌在方括号中。
路径表达式 | 结果 |
---|---|
/beans/bean[1] | 选取beans子元素下的第一个bean元素 |
/beans/bean[@id=“studentDao”] | 选取beans子元素下的属性值为studentDao的bean元素 |
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(File io);
Document document = saxReader.read(InputStream in);
· 这个两个入参具体使用哪一个看个人喜好,我本人比较喜欢传入InputStream,因为传入File对象需要给出文件相对项目的路径或者绝对路径,不太方便移植。而文件流可以直接从类加载器中获得,只需要保证项目在类路径下即可。
public class XMLTest{
private static InputStream in;
static{
in = XMLTest.class.getClassLoader().getResourceAsStream("beans.xml");
}
@Test
public testDom4j(){
...
}
}
- 获取所有的Bean标签,并获得其下面所有class属性值
- 获取id=studentSerivce的bean元素,并获得property的ref属性
- 得到beanTest元素的值
@Test
public void TestDom4j() throws Exception {
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(in);
//1.获得bean标签,并得到他们class的属性值
ArrayList<String> beans = new ArrayList<>();
//找到beans元素下的bean的节点
//List里面的元素应该是Node,但是Element对象继承了Node,因此可以强制转换
List<Element> beanList = document.selectNodes("/beans/bean");
for(Element bean:beanList){
beans.add(bean.attributeValue("class"));
//获取class属性值的第二种写法
// Attribute attr = bean.attribute("class");
// beans.add(attr.getStringValue());
}
System.out.println("bean中的class属性值为:" + beans);
//2.获取id=studentService的bean元素,并获得其下元素property的属性name值为studentDao的属性ref的值
//Node对象强制转化成Element
Element p01 = (Element)document.selectSingleNode("/beans/bean[@id='studentService']/property[@name='studentDao']");
System.out.println("property属性ref的值:" + p01.attributeValue("ref"));
//3.得到beanTest元素的值
Element beanTest = (Element) document.selectSingleNode("/beans/beanTest");
System.out.println("beanTest的值" + beanTest.getStringValue());
}
[TestNG] Running:
C:\Users\handsomestar\.IntelliJIdea2019.1\system\temp-testng-customsuite.xml
bean中的class属性值为:[com.memoforward.service.StudentService, com.memoforward.dao.StudentDao]
property属性ref的值:studentDao
beanTest的值beanTestValue
===============================================
Default Suite
Total tests run: 1, Failures: 0, Skips: 0
===============================================
· 解析XML文件还是相当简单的,其实就是一个树的读取,很快就能上手。