CRUD:
C:create R:retrieve U:update D:delete
XML解析技术概述
XML解析方式分为两种:DOM方式和SAX方式
- DOM:Document Object Model,文档对象模型。这种方式是W3C推荐的处理XML的一种方式。
- SAX:Simple API for XML。这种方式不是官方标准,属于开源社区XML-DEV,几乎所有的XML解析器都支持它。
两种解析方式的优缺点:
首先DOM解析XML也是在内存中形成一个树状结构。
DOM解析:
把整个XML文档先加载到内存中,形成树状结构。
缺点:如果文档非常大,加载到内存中容易产生内存溢出的问题。
优点:因为节点与节点之间有关系,进行增删改非常方便。
SAX解析:
基于事件驱动的,边读边解析。
缺点:不能进行增删改的操作。
优点:文档大也不会有内存溢出的问题,
- JAXP:是SUN公司推出的解析标准实现。
- Dom4J:是开源组织推出的解析开发包。(牛,大家都在用,包括SUN公司的一些技术的实现都在用)
- JDom:是开源组织推出的解析开发包。
JAXP
JAXP:(JAVA API for XML Processing)开发包是JavaSE的一部分,它由以下几个包及其子包组成:
- org.w3c.dom:提供DOM方式解析XML的标准接口。
- org.xml.sax:提供SAX方式解析XML的标准接口。
- javax.xml:提供了解析XML文档的类。
- DocumentBuilderFactory
- SAXParserFactory
获得JAXP中的DOM解析器
- 调用DocumentBuilderFactory.newInstance() 方法得到创建DOM解析器的工厂。
- 调用工厂对象的newDocumentBuilder方法得到DOM解析器对象。
- 调用DOM解析器对象的parse() 方法解析XML文档,得到代表整个文档的Document对象,进而可以利用Document对象对XML文档进行操作了。
DOM解析器在解析XML文档时,会把文档中的所有元素,按照其出现的层次关系,解析成一个个Node对象(节点)
在DOM中,节点之间的关系如下:
- 位于一个节点之上的节点是该节点的父节点(parent)
- 一个节点之下的节点是该节点的子节点(children)
- 同一层次,具有相同父节点的节点是兄弟节点(sibling)
- 一个节点的下一个层次的节点集合是节点后代(descendant)
- 父、祖父节点及所有位于节点上面的,都是节点的祖先(ancestor)
- Node对象提供了一系列常量来代表节点的类型,当获取某个Node类型后,就可以把Node节点转换成相应的节点对象(Node的子类对象),以便于调用其特有的方法(查看JDK文档)
- Node对象提供了相应的方法去获得它的父节点或子节点通过这些方法可以读取整个XML文档的内容、或添加、修改、删除XML文档的内容了。
具体步骤如下:
- 通过document.getElementsByTagName(" ") 可以获取节点集合,返回NodeList
- 通过Document.createElement(" ") 可以创建元素对象。
- Node对象中可以设置文本内容setTextContent()
- 通过Node的appendChild() 方法加入子节点
- 需要把内存中的DOM树形结构回写到XML文件中
- TransformerFactory工厂类创建Transformer回写类,通过transform(Source,Result)方法回写XML。
- NEW DOMSource(document) 和new StreamResult(xml)回写。
- 递归方法就是自己调用自己
<span style="font-size:18px;"><span style="font-size:18px;">public static void getNodeName(Node node){ if(node.getNodeType() == Node.ELEMENT_NODE){ System.out.println(node.getNodeName()); } NodeList nodeList = node.getChildNodes(); for(int i=0;i<nodeList.getLength();i++){ Node child = nodeList.item(i); getNodeName(child); } } </span></span>JAXP的DOM方式更新XML文档
javax.xml.transform包中的Transformer类用于把代表XML文件的Document对象转换成某种格式后进行输出,例如把XML文件应用样式表后转成一个HTML文档。利用这个对象,当然也可以把Document对象又重新写入到一个XML文件中。
Transformer类通过transform方法完成转换操作,该方法接收一个源和一个目的地。我们可以通过:
- javax.xml.transform.dom.DOMSource 类来关联要转换的document对象。
- 用javax.xml.transform.stream.StreamResult对象来表示数据的目的地。
示例如下:
book2.xml
<span style="font-size:18px;"><span style="font-size:18px;"><?xml version="1.0" encoding="UTF-8" standalone="no"?><书架> <书 编号="b2"> <书名>javaweb开发大全</书名> <作者>班长</作者> <售价>99.8元</售价> <简介>这是不错啊</简介> </书> <书> <书名>葵花宝典</书名> <作者>岳不群</作者> <售价>99.8两</售价> <简介>欲练此功...</简介> <猫>我是猫</猫></书> </书架></span></span>
JaxpDomTest.java
<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.jaxp; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import cn.itcast.utils.JaxpDomUtil; /** * JAXP的DOM解析XML * @author Administrator * */ public class JaxpDomTest { public static void main(String[] args) { try { run2(); } catch (Exception e) { e.printStackTrace(); } } /** * 获取作者的文本内容 * @throws Exception */ public static void run1() throws Exception{ // 获取解析器工厂类 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // 获取解析器对象 DocumentBuilder builder = factory.newDocumentBuilder(); // 解析XML的文档,返回document对象 Document document = builder.parse("src/book2.xml"); // 获取作者元素对象的集合,返回NodeList NodeList nodeList = document.getElementsByTagName("作者"); // 循环遍历,拿到每一个作者,打印文本的内容,getTextContent() for(int i=0;i<nodeList.getLength();i++){ Node node = nodeList.item(i); System.out.println(node.getTextContent()); } } /** * 需求:在第二本下,在末尾添加子节点 * @throws Exception */ public static void run2() throws Exception{ // 获取工厂类 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // 获取解析器 DocumentBuilder builder = factory.newDocumentBuilder(); // 解析xml,返回document对象 Document document = builder.parse("src/book2.xml"); // 获取第二本书 Node book2 = document.getElementsByTagName("书").item(1); // 创建元素对象 Element cat = document.createElement("猫"); // 设置文本内容 cat.setTextContent("我是猫"); // 把元素对象添加到第二本书下 book2.appendChild(cat); // 回写 // 创建回写类的工厂 TransformerFactory transformerFactory = TransformerFactory.newInstance(); // 获取回写类 Transformer transformer = transformerFactory.newTransformer(); // 调用回写的方法 transformer.transform(new DOMSource(document),new StreamResult("src/book2.xml")); } /** * 删除节点 * @throws Exception */ public static void run3() throws Exception{ String path = "src/book2.xml"; // 获取文档对象 Document document = JaxpDomUtil.getDocument(path); // 获取猫 Node cat = document.getElementsByTagName("猫").item(0); // 获取书(猫的父节点) Node book2 = cat.getParentNode(); // 通过书删除猫 book2.removeChild(cat); // 回写 JaxpDomUtil.writeXML(document,path); } }</span></span>封装回写和获取Document的工具类:JAXPDOMUtil.java
<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.utils; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; /** * JAPTDOM解析的工具类 * @author Administrator * */ public class JaxpDomUtil { /** * 通过path获取document对象 * @param path * @return * @throws Exception */ public static Document getDocument(String path) throws Exception{ // 获取工厂类 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // 获取解析器对象 DocumentBuilder builder = factory.newDocumentBuilder(); // 解析xml return builder.parse(path); } /** * 回写方法 * @param document * @param path * @throws Exception */ public static void writeXML(Document document,String path) throws Exception{ // 获取回写类的工厂 TransformerFactory factory = TransformerFactory.newInstance(); // 获取回写类 Transformer transformer = factory.newTransformer(); // 回写 transformer.transform(new DOMSource(document),new StreamResult(path)); } } </span></span>
SAX解析(JAVA中的JAXP有SAX解析)
- 在使用DOM解析XML文档时,需要读取整个XML文件,在内存中架构代表整个DOM树的Document对象,从而再对XML文档进行操作。此种情况下,如果XML文档特别大就会消耗计算机的大量内存,并且容易导致内存溢出。
- SAX解析允许在读取文档时,即时对文档进行处理,而不必等到整个文档装载完才会对文档进行操作。
- SAX采用事件处理的方式解析XML文件,利用 SAX 解析 XML 文档,涉及两个部分:解析器和事件处理器(通过API和图)
解析器可以使用JAXP的API创建,创建出SAX解析器后,就可以指定解析器去解析某个XML文档。
解析器采用SAX方式在解析某个XML文档时,它只要解析到XML文档的一个组成部分,都会去调用事件处理器的一个方法,解析器在调用事件处理器的方法时,会把当前解析到的xml文件内容作为方法的参数传递给事件处理器。
事件处理器由程序员编写,程序员通过事件处理器中方法的参数,就可以很轻松地得到sax解析器解析到的数据,从而可以决定如何对数据进行处理。
SAX 是事件驱动的 XML 处理方法
- 它是基于事件驱动的
- startElement() 回调在每次 SAX 解析器遇到元素的起始标记时被调用
- characters() 回调为字符数据所调用
- endElement() 为元素的结束标记所调用
- DefaultHandler类(在 org.xml.sax.helpers 软件包中)来实现所有这些回调,并提供所有回调方法默认的空实现
SAX的事件驱动模型
SAX DocumentHandler示例
SAX 解析器采用了基于事件的模型,它在解析XML文档的时候可以触发一系列的事件
发生相应事件时,将调用一个回调方法
- Start document
- Start element (config) <?xml version=“1.0” encoding=“utf-8”?>
- Characters (whitespace) <config>
- Start element (server) <server>UNIX</server>
- Characters (UNIX) </config>
- End element (server)
- Characters (whitespace)
- End element (config)
- End document
使用SAX方式解析XML
(1)使用SAXParserFactory创建SAX解析工厂
SAXParserFactory spf = SAXParserFactory.newInstance();
(2)通过SAX解析工厂得到解析器对象
SAXParser sp = spf.newSAXParser();
(3)通过解析器对象解析XML文件
xmlReader.parse("book.xml",new XMLContentHandler());
注意这里的XMLContentHandler继承DefaultHandler
SAX举例说明
public class XMLContentHandler extends DefaultHandler{
//当前元素中的数据
private String currentData;
//取得元素数据
public void characters(char[] ch,int start,int length)
throws SAXException {
currentData=new String(ch,start,length);
}
//在解析整个文档结束时调用
public void endDocument() throws SAXException {
System.out.println("结束文档");
}
//在解析元素结束时调用
public void endElement(String uri,String localName,String name)
throws SAXException {
System.out.println("节点数据 *************************"+this.currentData);
System.out.println("结束元素 ************"+name);
}
//在解析整个文档开始时调用
public void startDocument() throws SAXException {
System.out.println("开始文档");
}
//在解析元素开始时调用
public void startElement(String uri,String name,
Attributes attributes) throws SAXException {
System.out.println("开始元素 ************"+name);
}
}
JAXP的SAX解析XML文档示例如下:
<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.jaxp.sax; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** * SAX的入门 * @author Administrator * */ public class JaxpSaxTest { public static void main(String[] args) { try { run1(); } catch (Exception e) { e.printStackTrace(); } } /** * 获取所有的解析的内容 * @throws Exception * @throws ParserConfigurationException */ public static void run1() throws Exception{ // 获取SAX的解析器工厂 SAXParserFactory factory = SAXParserFactory.newInstance(); // 获取解析器 SAXParser parser = factory.newSAXParser(); // 解析 parser.parse("src/book2.xml",new MyHandler2()); } } /** * 获取作者标签的文本内容 * */ class MyHandler2 extends DefaultHandler{ // 如果解析到作者标签的时候,flag设置成true private boolean flag = false; private int count = 0; /** * 默认解析开始标签,默认调用该方法 */ public void startElement(String uri,String qName,Attributes attributes) throws SAXException { // 如果要是作者标签,flag设置成true if("作者".equals(qName)){ flag = true; count++; } } /** * 能控制characters的输出,我只在解析作者标签的时候,才打印 */ public void characters(char[] ch,int length) throws SAXException { // 如果flag是true,就打印 // 每一次都打印 if(flag && count == 1){ String str = new String(ch,length); System.out.println(str); } } /** * */ public void endElement(String uri,String qName) throws SAXException { // flag恢复成false flag = false; } } /** * 自己事件处理器 * 重写三方法 * @author Administrator * */ class MyHandler extends DefaultHandler{ /** * 只要一解析到开始标签的时候,默认调用该方法,把解析的内容赋值给参数。 */ public void startElement(String uri,Attributes attributes) throws SAXException { System.out.println("开始标签:"+qName); } /** * 只要解析到文本的内容,默认调用该方法 */ public void characters(char[] ch,int length) throws SAXException { String str = new String(ch,length); System.out.println(str); } /** * 解析到结束标签的时候,默认调用方法,把解析的内容赋值给参数。 */ public void endElement(String uri,String qName) throws SAXException { System.out.println("结束标签:"+qName); } </span><h2><span style="font-size:18px;">}</span></h2></span>
DOM4J解析XML文档
- Dom4j是一个简单、灵活的开放源代码的库。Dom4j是由早期开发JDOM的人分离出来而后独立开发的。与JDOM不同的是,dom4j使用接口和抽象基类,虽然Dom4j的API相对要复杂一些,但它提供了比JDOM更好的灵活性。
- Dom4j是一个非常优秀的Java XML API,具有性能优异、功能强大和极易使用的特点。现在很多软件采用的Dom4j,例如Hibernate,包括sun公司自己的JAXM也用了Dom4j。
- 使用Dom4j开发,需下载dom4j相应的jar文件。
DOM4J的使用技巧一:查找标签文本
DOM4J使用技巧二:在指定的节点中添加子节点
- 创建解析器 new SAXReader()
- 解析xml read()
- 获取根节点 getRootElement()
- 获取所有的指定标签的集合 root.elements(标签名)
- 直接调用addElement()设置子节点。
- 使用setText()设置文本
- 回写xml文件
format.setEncoding("UTF-8");
XMLWriter writer = new XMLWriter(new FileOutputStream("src/book2.xml"),format);
writer.write(document);
writer.close();
DOM4J使用技巧三:在指定的位置新增节点
DOM4J使用技巧四:修改节点文本和删除节点
DOM4J使用技巧五:DOM4J对XPATH的支持
提供了一种书写方式,可以很方便的找到XML标签(xpath属于第三方包可以通过xpath文档学习XPATH的语法)
(1)DOM4J使用XPATH有两种方法
- selectNodes()。
- selectSingleNode()可以直接使用document.selectNodes(xpath),不再需要先获取根节点了。非常的方便
jaxen-1.1-beta-6.jar
关于XPATH的语法实例如下(常用的有如下三个实例,具体的可以查询文档):
实例1:基本的XPath语法类似于在一个文件系统中定位文件,如果路径以斜线 / 开始,那么该路径就表示到一个元素的绝对路径
实例二:如果路径以双斜线 // 开头,则表示选择文档中所有满足双斜线//之后规则的元素(无论层级关系)
实例三:星号 * 表示选择所有由星号之前的路径所定位的元素
DOM4J使用技巧六:DOM4J获取Document对象
DOM4J使用技巧七:节点对象
(6)在某节点下添加子节点Element ageElm = newMemberElm.addElement("age");
DOM4J使用技巧八:将文档写入XML文件
DOM4J使用技九:在指定位置插入节点
DOM4J使用技十:字符串与XML转换
<?xml version="1.0" encoding="UTF-8"?> <书架> <书> <书名>javaweb开发大全</书名> <作者>班长</作者> <售价>99.8元</售价> <简介>这是不错啊</简介> </书> <书> <书名>葵花宝典</书名> <狗>小狗</狗> <作者>岳不群</作者> <售价>99.8两</售价> <简介>欲练此功...</简介> </书> </书架>
package cn.itcast.dom4j; import java.io.FileOutputStream; import java.util.List; import org.dom4j.Document; import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.dom4j.Node; import org.dom4j.io.OutputFormat; import org.dom4j.io.SAXReader; import org.dom4j.io.XMLWriter; /** * DOM4J的解析XML * @author Administrator * */ public class Dom4jTest { public static void main(String[] args) { try { run1(); } catch (Exception e) { e.printStackTrace(); } } /** * 对XPATH的支持 * @throws Exception */ public static void run6() throws Exception{ // 获取解析器对象 SAXReader reader = new SAXReader(); // 解析XML Document document = reader.read("src/book2.xml"); // List<Node> list = document.selectNodes("/书架/书/作者"); List<Node> list = document.selectNodes("//作者"); Node author2 = list.get(1); System.out.println(author2.getText()); } /** * 修改文本内容 * @throws Exception */ public static void run5() throws Exception{ // 获取解析器对象 SAXReader reader = new SAXReader(); // 解析XML Document document = reader.read("src/book2.xml"); // 获取根节点 Element root = document.getRootElement(); // 获取狗的节点 Element book2 = (Element) root.elements("书").get(1); Element dog = book2.element("狗"); dog.setText("小狗"); // 回写 // 回写 OutputFormat format = OutputFormat.createPrettyPrint(); // 回写 XMLWriter writer = new XMLWriter(new FileOutputStream("src/book2.xml"),format); writer.write(document); writer.close(); } /** * 删除子节点 * 删除第二本书下的猫节点 */ public static void run4() throws Exception{ // 获取解析器对象 SAXReader reader = new SAXReader(); // 解析XML Document document = reader.read("src/book2.xml"); // 获取根节点 Element root = document.getRootElement(); // 获取猫 Element book2 = (Element) root.elements("书").get(1); Element cat = book2.element("猫"); // 通过猫获取猫的父节点 // cat.getParent(); // 通过父节点删除猫 book2.remove(cat); // 回写 OutputFormat format = OutputFormat.createPrettyPrint(); // 回写 XMLWriter writer = new XMLWriter(new FileOutputStream("src/book2.xml"),format); writer.write(document); writer.close(); } /** * 在第二本书的作者标签之前添加团购价的标签 * @throws Exception */ public static void run3() throws Exception{ // List // 获取解析器对象 SAXReader reader = new SAXReader(); // 解析XML Document document = reader.read("src/book2.xml"); // 获取根节点 Element root = document.getRootElement(); // 获取第二本书 Element book2 = (Element) root.elements("书").get(1); // 获取书下的所有子节点,返回List集合 List<Element> list = book2.elements(); // 创建元素对象 DocumentHelper.createElement("狗") Element dog = DocumentHelper.createElement("狗"); dog.setText("大狗"); // list.add(index,Element); list.add(1,dog); OutputFormat format = OutputFormat.createPrettyPrint(); // 回写 XMLWriter writer = new XMLWriter(new FileOutputStream("src/book2.xml"),format); writer.write(document); writer.close(); } /** * 在第二本书下添加子节点 */ public static void run2() throws Exception{ // 获取解析器对象 SAXReader reader = new SAXReader(); // 解析XML,返回Document对象 Document document = reader.read("src/book2.xml"); // 获取根节点 Element root = document.getRootElement(); // 获取第二本书 Element book2 = (Element) root.elements("书").get(1); // 可以直接在第二本书下添加子节点,设置文本内容 book2.addElement("猫").setText("我是猫"); // 回写 // 创建漂亮的格式 OutputFormat format = OutputFormat.createPrettyPrint(); //OutputFormat format = OutputFormat.createCompactFormat(); // 设置编码 format.setEncoding("UTF-8"); // 回写类 XMLWriter writer = new XMLWriter(new FileOutputStream("src/book2.xml"),format); // 回写了文档对象 writer.write(document); // 关闭流 writer.close(); } /** * 获取第二本书作者的文本内容 * @throws Exception */ public static void run1() throws Exception{ // 获取解析器对象 SAXReader reader = new SAXReader(); // 解析XML,返回Document对象 Document document = reader.read("src/book2.xml"); // 获取根节点(书架标签) Element root = document.getRootElement(); // 获取书的节点,获取第二本书 List<Element> books = root.elements("书"); Element book2 = books.get(1); // 获取作者的标签 Element author2 = book2.element("作者"); // 获取文本内容 System.out.println(author2.getText()); } }
XML 约束之Schema
XML Schema也是一种用于定义和描述XML文档结构与内容的模式语言,其出现是为了克服DTD的局限性。<?xml version="1.0" encoding="UTF-8" ?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www. itcast.cn" elementFormDefault="qualified"> <xs:element name='书架' > <xs:complexType> <xs:sequence maxOccurs='unbounded' > <xs:element name='书' > <xs:complexType> <xs:sequence> <xs:element name='书名' type='xs:string' /> <xs:element name='作者' type='xs:string' /> <xs:element name='售价' type='xs:string' /> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>
<?xml version="1.0" encoding="UTF-8"?> <itcast:书架 xmlns:itcast="http://www.itcast.cn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=“http://www.itcast.cn book.xsd"> <itcast:书> <itcast:书名>九阴真经</itcast:书名> <itcast:作者>郭靖</itcast:作者> <itcast:售价>28.00元</itcast:售价> </itcast:书> </itcast:书架>在XML Schema文档中声明名称空间。
targetNamespace="http://www. itcast.cn"
elementFormDefault="qualified"
attributeFormDefault="qualified"
>
<xs:schema>