XML学习教程
xml是什么
xml是可扩展标记语言EXtensible Markup Language
XML 被设计用来传输和存储数据。
XML 不会做任何事情。XML 被设计用来结构化、存储以及传输信息。
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>
第一行是 XML 声明。它定义 XML 的版本 (1.0) 和所使用的编码 (ISO-8859-1 = Latin-1/西欧字符集)。
下一行描述文档的根元素(像在说:“本文档是一个便签”):
接下来 4 行描述根的 4 个子元素(to, from, heading 以及 body):
最后一行定义根元素的结尾:
XML 没什么特别的。它仅仅是纯文本而已。有能力处理纯文本的软件都可以处理 XML。
不过,能够读懂 XML 的应用程序可以有针对性地处理 XML 的标签。标签的功能性意义依赖于应用程序的特性。
上例中的标签没有在任何 XML 标准中定义过(比如 和 )。这些标签是由文档的创作者发明的。
这是因为 XML 没有预定义的标签。
在 HTML 中使用的标签(以及 HTML 的结构)是预定义的。HTML 文档只使用在 HTML 标准中定义过的标签(比如
、
等等)。
XML 允许创作者定义自己的标签和自己的文档结构。
XML 标签对大小写敏感。
xml使用场景
数据交互
虽然目前有json来替代xml作数据交互,但是对于一些老系统或者某些软件来说,xml的优先级还是更高
配置文件(mybatis)
java操作xml
可使用技术
写操作
dom、dom4j、jdom、xstream、jaxb、spring ixm
读操作
sax、deqister、dom、dom4j、jdom、xstream、jaxb、spring ixm
术语解释
jaxp:java API For XML Process
java关于xml的api,关注如何站到某个元素内容
主要由两类:
-
dom:Document Object Model
- w3c推出的规范
-
sax:Simple API For XML Binding
jaxb:Java Architecture for XML Binding
关注xml和java Object之间的转化
oxm:Object XML Mapping
对象和xml间映射
具体应用
DOM
dom是jdk自带的xml处理程序。
DOM 分析器通过对XML文档的分析,把整个XML文档以一棵DOM树的形式存放在内存中,应用程序可以随时对DOM树中的任何一个部分进行访问与操作,也就是 说,通过DOM树,应用程序可以对XML文档进行随机访问。这种访问方式给应用程序的开发带来了很大的灵活性,它可以任意地控制整个XML文档中的内容。 然而,由于DOM分析器把整个XML文档转化成DOM树放在了内存中,因此,当XML文档比较大或者文档结构比较复杂时,对内存的需求就比较高。而且,对 于结构复杂的树的遍历也是一项比较耗时的操作。所以,DOM分析器对机器性能的要求比较高,实现效率不十分理想。
public void testRead() throws ParserConfigurationException, IOException, SAXException {
//创建DocumentBuilder工厂
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
//通过工厂获得DocumentBuilder
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
//通过DocumentBuilder获得
Document parse = documentBuilder.parse("src\\main\\resources\\test.xml");
NodeList list = parse.getElementsByTagName("link");
for (int i = 0; i < list.getLength(); i++) {
Element item = (Element) list.item(i);
System.out.println(item.getAttribute("type"));
System.out.println(item.getTagName());
System.out.println(item.getFirstChild().getNodeValue());
}
}
dom会将xml解析成下面这样的树。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eCBG3c6d-1636353899163)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211015142905106.png)]
通过xpath也可以查找到元素
@Test
public void testReadByXpath() throws Exception {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document = documentBuilder.parse("src\\main\\resources\\test.xml");
XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xPath = xPathFactory.newXPath();
XPathExpression compile = xPath.compile("//link[@type='0']//text()");
Node node = (Node) compile.evaluate(document, XPathConstants.NODE);
System.out.println(node.getNodeValue());
}
通过dom也可以将对象转化成xml
@Test
public void testWrite() throws Exception {
Document document = creatDocument();
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource domSource = new DOMSource(document);
FileOutputStream fileOutputStream = new FileOutputStream("src\\main\\resources\\test2.xml");
StreamResult streamResult = new StreamResult(fileOutputStream);
//设置字符编码
transformer.setOutputProperty(OutputKeys.ENCODING,"utf-8");
//设置美化换行
transformer.setOutputProperty(OutputKeys.INDENT,"yes");
transformer.transform(domSource,streamResult);
fileOutputStream.close();
}
mybatis中的应用
org.apache.ibatis.session.SqlSessionFactoryBuilder中的build方法会调用parser.passer()方法,最后的调用是this.xpath.evaluate(expression, root, returnType);,使用xpath进行解析。(mybatis版本3.5.3)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yPWgwGYL-1636353899165)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211015171118118.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dVdQWlCO-1636353899172)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211015171213225.png)]
SAX
SAX分析器在对XML文档进行分析时,触发一系列的事件,应用程序通过事件处理函数实现对XML文档的访问。由于事件触发本身是有时序性的,因此,SAX分析器提供的是一种对XML文档的顺序访问机制,对于已经分析过的部分,不能再倒回去重新处理。
适用于只处理文件中的数据而不需要对文件做更改的情况。
使用sax处理重写DefaultHandler()类来处理数据
public void saxTest()throws Exception{
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
SAXParser saxParser = saxParserFactory.newSAXParser();
saxParser.parse("src\\main\\resources\\test.xml",new DefaultHandler(){
@Override
public void startDocument() throws SAXException {
System.out.println("startDocument");
}
@Override
public void endDocument() throws SAXException {
System.out.println("endDocument");
}
@Override
public void startElement(String uri, String localName,
String qName, Attributes attributes) throws SAXException {
System.out.print("<"+qName);
for (int i = 0;i<attributes.getLength();i++){
System.out.print(" "+attributes.getQName(i)+"=\""+attributes.getValue(i)+"\"");
}
System.out.print(">");
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
System.out.print("</"+qName+">");
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
System.out.print(new String(ch,start,length));
}
});
}
处理结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hb1PeFLs-1636353899173)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211018100451886.png)]
jdom
JDOM是处理XML的纯JAVA API。使用具体类而不用接口,既要生成大多数节点类型的实例,只要将一两个变元传入即可。是目前表现优秀的处理XML的JAVA API。
public static void parseXmlByJDOM(){
long begintime=System.currentTimeMillis();
File f=new File("students.xml");
SAXBuilder builder=new SAXBuilder();
try {
Document doc=builder.build(new FileInputStream(f));
Element root=doc.getRootElement();
System.out.println(root.getAttributeValue("class")+"-人数:--"+root.getAttributeValue("count"));
List list=root.getChildren("student");
for(int i=0;i<list.size();i++){
Element ele=(Element)list.get(i);
System.out.println("第"+(i+1)+"个学生");
System.out.println(ele.getChildText("name")+"---"+ele.getChildText("age"));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("sax解析时间(毫秒)"+(System.currentTimeMillis()-begintime));
}
dom4j
DOM4J 是一个非常非常优秀的Java XML API,具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的软件。如今你可以看到越来越多的 Java 软件都在使用 DOM4J 来读写 XML,特别值得一提的是连 Sun 的 JAXM 也在用 DOM4J。
目前org.dom4j还在维护,推荐使用orj.dom4j
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
在dom4j中xml也是采用树状存储
public void dom4jTestRead() throws Exception{
SAXReader saxReader = new SAXReader();
org.dom4j.Document document = saxReader.read(new File("src\\main\\resources\\test.xml"));
org.dom4j.Element rootElement = document.getRootElement();
System.out.println(rootElement);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CV4jUsQW-1636353899174)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211018103959202.png)]
DOM4J是 dom4j.org 出品的一个开源 XML 解析包。DOM4J应用于 Java 平台,采用了 Java 集合框架并完全支持 DOM,SAX 和 JAXP。
DOM4J 使用起来非常简单。只要你了解基本的 XML-DOM 模型,就能使用。
Dom:把整个文档作为一个对象。
DOM4J 最大的特色是使用大量的接口。它的主要接口都在org.dom4j里面定义:
Attribute | 定义了 XML 的属性。 |
---|---|
Branch | 指能够包含子节点的节点。如XML元素(Element)和文档(Docuemnts)定义了一个公共的行为 |
CDATA | 定义了 XML CDATA 区域 |
CharacterData | 是一个标识接口,标识基于字符的节点。如CDATA,Comment, Text. |
Comment | 定义了 XML 注释的行为 |
Document | 定义了XML 文档 |
DocumentType | 定义 XML DOCTYPE 声明 |
Element | 定义XML 元素 |
ElementHandler | 定义了Element 对象的处理器 |
ElementPath | 被 ElementHandler 使用,用于取得当前正在处理的路径层次信息 |
Entity | 定义 XML entity |
Node | 为dom4j中所有的XML节点定义了多态行为 |
NodeFilter | 定义了在dom4j 节点中产生的一个滤镜或谓词的行为(predicate) |
ProcessingInstruction | 定义 XML 处理指令 |
Text | 定义 XML 文本节点 |
Visitor | 用于实现 Visitor模式 |
XPath | 在分析一个字符串后会提供一个 XPath 表达式 |
接口之间的继承关系如下:
interface java.lang.Cloneable
interface org.dom4j.Node
interface org.dom4j.Attribute
interface org.dom4j.Branch
interface org.dom4j.Document
interface org.dom4j.Element
interface org.dom4j.CharacterData
interface org.dom4j.CDATA
interface org.dom4j.Comment
interface org.dom4j.Text
interface org.dom4j.DocumentType
interface org.dom4j.Entity
interface org.dom4j.ProcessingInstruction
遍历xml节点
对Document对象调用getRootElement()方法可以返回代表根节点的Element对象。拥有了一个Element对象后,可以对该对象调用elementIterator()方法获得它的子节点的Element对象们的一个迭代器。使用(Element)iterator.next()方法遍历一个iterator并把每个取出的元素转化为Element类型。
public boolean isOnly(String catNameEn, HttpServletRequest request,
String xml) {
boolean flag = true;
String path = request.getRealPath("");
Document doc = load(path + "/" + xml);
Element root = doc.getRootElement();
for (Iterator i = root.elementIterator(); i.hasNext();) {
Element el = (Element) i.next();
if (catNameEn.equals(el.elementTextTrim("engName"))) {
flag = false;
break;
}
}
return flag;
}
总结:
dom
优点:
形成树状结构,直观,容易理解
整棵树加载到内存,允许对XML文档进行随机访问
解析过程存储在内存中,方便修改
缺点:
整个XML文档必须一次解析完
当文件较大时对内存消耗较大
一般的DOM节点对于必须为所有节点创建对象的对象类型绑定不太理想
最适合于:
需要修改XML文档的应用程序或XSLT应用程序(不可用于只读XML的应用程序)
sax
优点:
采用事件驱动,无需将整个文档加载到内存,因而内存消耗少
适用于只需要处理xml中数据时
堆模型允许注册多个ContentHandler
缺点:
没有内置的文档导航支持
不易编码
不能够随机访问XML文档
很难同时访问同一个xml中的多处不同数据
不支持名字空间作用域
最适合于:
只从XML读取数据的应用程(不可用于操作或修改XML文档)
JDOM
优点:
是基于树的处理XML的Java API,把树加载在内存中
没有向下兼容的限制,因此比DOM简单
速度快,缺陷少
具有SAX的JAVA规则
缺点:
不能处理大于内存的文档
JDOM表示XML文档逻辑模型。不能保证每个字节真正变换。
针对实例文档不提供DTD与模式的任何实际模型。
不支持与DOM中相应遍历包
最适合于:
JDOM具有树的便利,也有SAX的JAVA规则。在需要平衡时使用
dom4j
优点:
大量使用了Java集合类,方便Java开发人员,同时提供一些提高性能的替代方法。
支持XPath。
有很好的性能.
缺点:
大量使用了接口,API较为复杂。
xpath语法
1、选取节点
XPath 使用路径表达式在 XML 文档中选取节点,节点是沿着路径或者 step 来选取的。
常见的路径表达式:
表达式 | 描述 |
---|---|
nodename | 选取当前节点的所有子节点 |
/ | 从根节点选取 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置 |
. | 选取当前节点 |
… | 选取当前节点的父节点 |
@ | 选取属性 |
实例
路径表达式 | 结果 |
---|---|
bookstore | 选取 bookstore 元素的所有子节点 |
/bookstore | 选取根元素 bookstore |
bookstore/book | 选取bookstore 下名字为 book的所有子元素。 |
//book | 选取所有 book 子元素,而不管它们在文档中的位置。 |
bookstore//book | 选取bookstore 下名字为 book的所有后代元素,而不管它们位于 bookstore 之下的什么位置。 |
//**@**lang | 选取所有名为 lang 的属性。 |
2、谓语(Predicates)
谓语用来查找某个特定的节点或者包含某个指定的值的节点。
谓语被嵌在方括号中。
实例
常见的谓语的一些路径表达式:
路径表达式 | 结果 |
---|---|
/bookstore/book[1] | 选取属于 bookstore 子元素的第一个 book 元素。 |
/bookstore/book[last()] | 选取属于 bookstore 子元素的最后一个 book 元素。 |
/bookstore/book[last()-1] | 选取属于 bookstore 子元素的倒数第二个 book 元素。 |
/bookstore/book[position()< 3] | 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。 |
//title[@lang] | 选取所有拥有名为 lang 的属性的 title 元素。 |
//title[@lang=‘eng’] | 选取所有 title 元素,要求这些元素拥有值为 eng 的 lang 属性。 |
/bookstore/book[price>35.00] | 选取所有 bookstore 元素的 book 元素,要求book元素的子元素 price 元素的值须大于 35.00。 |
/bookstore/book[price>35.00]/title | 选取所有 bookstore 元素中的 book 元素的 title 元素,要求book元素的子元素 price 元素的值须大于 35.00 |
3、选取未知节点
XPath 通配符可用来选取未知的 XML 元素。
通配符 | 描述 |
---|---|
* | 匹配任何元素节点 |
@* | 匹配任何属性节点 |
node() | 匹配任何类型的节点 |
实例
路径表达式 | 结果 |
---|---|
/bookstore/* | 选取 bookstore 元素的所有子节点 |
//* | 选取文档中的所有元素 |
//title[@*] | 选取所有带有属性的 title 元素。 |
4、选取若干路径
通过在路径表达式中使用“|”运算符,您可以选取若干个路径。
实例
路径表达式 | 结果 |
---|---|
//book/title | //book/price | 选取所有 book 元素的 title 和 price 元素。 |
//title | //price | 选取所有文档中的 title 和 price 元素。 |
/bookstore/book/title**|**//price | 选取所有属于 bookstore 元素的 book 元素的title 元素,以及文档中所有的 price 元素。 |
5、XPath 轴
轴可定义某个相对于当前节点的节点集。
轴名称 | 结果 |
---|---|
ancestor | 选取当前节点的所有先辈(父、祖父等) |
ancestor-or-self | 选取当前节点的所有先辈(父、祖父等)以及当前节点本身 |
attribute | 选取当前节点的所有属性 |
child | 选取当前节点的所有子元素。 |
descendant | 选取当前节点的所有后代元素(子、孙等)。 |
descendant-or-self | 选取当前节点的所有后代元素(子、孙等)以及当前节点本身。 |
following | 选取文档中当前节点的结束标签之后的所有节点。 |
namespace | 选取当前节点的所有命名空间节点 |
parent | 选取当前节点的父节点。 |
preceding | 选取文档中当前节点的开始标签之前的所有节点。 |
preceding-sibling | 选取当前节点之前的所有同级节点。 |
self | 选取当前节点。 |
6、路径
位置路径可以是绝对的,也可以是相对的。
绝对路径起始于正斜杠( / ),而相对路径不会这样。在两种情况中,位置路径均包括一个或多个步,每个步均被斜杠分割:
/step/step/...
step/step/...
每个步均根据当前节点集之中的节点来进行计算。
轴(axis):定义所选节点与当前节点之间的树关系
节点测试(node-test):识别某个轴内部的节点
零个或者更多谓语(predicate):更深入地提炼所选的节点集
步的语法:轴名称::节点测试[谓语]
实例
例子 | 结果 |
---|---|
child::book | 选取所有属于当前节点的子元素的 book 节点 |
attribute::lang | 选取当前节点的 lang 属性 |
child:: * | 选取当前节点的所有子元素 |
attribute:: * | 选取当前节点的所有属性 |
child::text() | 选取当前节点的所有文本子节点 |
child::node() | 选取当前节点的所有子节点 |
descendant::book | 选取当前节点的所有 book 后代 |
ancestor::book | 选择当前节点的所有 book 先辈 |
ancestor-or-self::book | 选取当前节点的所有book先辈以及当前节点(假如此节点是book节点的话) |
child:: */child::price | 选取当前节点的所有 price 孙。 |
7、XPath 运算符
运算符 | 描述 | 实例 | 返回值 |
---|---|---|---|
| | 计算两个节点集 | //book | //cd | 返回所有带有 book 和 ck 元素的节点集 |
+ | 加法 | 6 + 4 | 10 |
- | 减法 | 6 - 4 | 2 |
* | 乘法 | 6 * 4 | 24 |
div | 除法 | 8 div 4 | 2 |
= | 等于 | price=9.80 | 如果 price 是 9.80,则返回 true。如果 price 是 9.90,则返回 fasle。 |
!= | 不等于 | price!=9.80 | 如果 price 是 9.90,则返回 true。如果 price 是 9.80,则返回 fasle。 |
< | 小于 | price<9.80 | 如果 price 是 9.00,则返回 true。如果 price 是 9.90,则返回 fasle。 |
<= | 小于或等于 | price<=9.80 | 如果 price 是 9.00,则返回 true。如果 price 是 9.90,则返回 fasle。 |
> | 大于 | price>9.80 | 如果 price 是 9.90,则返回 true。如果 price 是 9.80,则返回 fasle。 |
>= | 大于或等于 | price>=9.80 | 如果 price 是 9.90,则返回 true。如果 price 是 9.70,则返回 fasle。 |
or | 或 | price=9.80 or price=9.70 | 如果 price 是 9.80,则返回 true。如果 price 是 9.50,则返回 fasle。 |
and | 与 | price>9.00 and price<9.90 | 如果 price 是 9.80,则返回 true。如果 price 是 8.50,则返回 fasle。 |
mod | 计算除法的余数 | 5 mod 2 | 1 |