XML学习笔记(四):xml解析详解以及使用 DOM和SAX 解析XML :
来源:互联网 发布:java 读文本文件 编辑:程序博客网 时间:2024/03/29 19:40
一、XML解析的简介
1、一般来说xml解析的方式有两种,Dom与SAX,且各有优缺点。pull是第三种方法见:pull解析XML
1)DOM:dom对象树
①、整个XML放入内存,转换为Document对象;
②、每个元素,转换成Element对象
③、文本,转换成Text对象
④、属性,转换成Attribute对象
优缺点:元素的CRUD比较简单,但是内存消耗大,大的xml文档不适合。
补充:内存不足时,可以修改参数,调整JVM的内存大小
1、java.lang.OutOfMemoryError: Java heap space 堆内存不足
2、单个main方法运行时,修改VM Arguments参数为 -Xmx80m 。80为随意修改。3、运行程序时,可以修改eclipse的安装目录中 eclipse.ini文件的参数值: -Xmx768m 。
2)SAX:
对于整个XML文档,从上往下读,读取一行,解析一行。
优缺点:不影响内存,但是不适合对文档作CRUD。
2、Java提供两块API与xml解析有关:
1)一为Jaxp:提供了dom与sax解析xml的方法(完全可以被Jdom或是Dom4J代替);
2)二是jaxb:主要是使用注解处理xml与JavaBean的映射,在WebService中被广泛应用。
3、我们在实际开发环境中,对于XML的工作一般可以总结为几类(都会解决):
1)配置文件的解析:自己本地的文件解析;(XML学习笔记(五):使用 jdom和dom4j 解析XML)
2)数据交互:与第三方平台如淘宝、天猫、网易等的交互数据的解析;(XML学习笔记(六):Jaxb负责xml与javaBean映射)
3)模板数据:如短息或是邮件模板;
4、FreeMarker和Apache有一套不错的api可以用于XML的解析,分别为freemarker.template.Template类和org.apache.velocity.VelocityContext类。(XML学习笔记(七):使用freemark与apache填充xml模板)
二、Jaxp的Dom和Sax
1、XML文件存放在名叫resources的系统source filder文件夹内:命名为Request.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?><REQUEST><appnt><name>SAM-SHO</name><age>28</age></appnt><insured age="24" name="SAMMY" /> <!-- 正确 --><product><!-- 输出不一样 --><name>牙膏</name><price>100</price><disc>刷牙用的</disc></product><Service><RequestHandle><Handle name="log" type="com.xml.base.LogHandle1"><init-param><param-name>file</param-name><param-value>d:/log.txt</param-value></init-param><init-param><param-name>filegg4</param-name><param-value>d:/log.txtgg4</param-value></init-param></Handle><Handle name="log2" type="com.xml.base.LogHandle1.2"><init-param><param-name>file2</param-name><param-value>d:/log.txt2</param-value></init-param></Handle></RequestHandle><RequestHandle><Handle name="log1" type="com.xml.base.LogHandle1.1"><init-param><param-name>file1</param-name><param-value>d:/log.txt1</param-value></init-param></Handle></RequestHandle></Service><Service name="EasyQueryUI" type="com.xml.base.LogHandle2"><RequestHandle><Handle name="Sql" type="com.xml.base.LogHandle3"></Handle></RequestHandle></Service></REQUEST>
2、使用Dom和Sax解析Xml,其其开始的步骤基本都是统一的:
1)都是创建工厂、获取解析器、获取文件、解析文件。
2)然后不同之处就开始了。Dom是直接取转换的对象,而Sax是在自定义的Handler中逐个处理获取Element。
3)解析的代码都比较简单,直接上实例代码。
(一)DOM
1、获取XML文档的元素、属性、文本信息、创建新的元素并写入文档。方法如下:
1)获取工厂、解析器等为固定步骤,获取xml文档的方法有多种,这边使用classloader方式。
2)整个XML文档被解析成一个Document对象。
3)获取元素:document.getElementsByTagName("appnt"), 可以获取名为appnt的元素。这边注意,Node为一切元素的父类。
4)getNodeName():获元素的名称;getTextContent():获取元素的文本值;getAttribute():获取元素的属性值
5)添加元素的用法是固定的,
①、先创建元素,并添加属性与文本内容,方法为createElement("元素名称");setAttribute();setTextContent()
②、规定放入的位置,如放入根节点:root.appendChild("创建的元素"),root为根节点。
③、固定使用Transformer类把创建的新元素,写入文件。
/** * 通过DOM获取xml文档 */public void getXMLFromDom() {try {// 1-: 获得dom解析器工厂(工作的作用是用于创建具体的解析器)DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();// 2-:获得具体的dom解析器DocumentBuilder db = dbf.newDocumentBuilder();// 3-: 解析一个xml文档,获得Document对象// 利用classloader加载xml文件,文件的位置很重要InputStream input = this.getClass().getClassLoader().getResourceAsStream("Request.xml");Document document = db.parse(input);// 4-通过元素名,得到某个元素// 4-1 得到<appnt>元素NodeList appntElement = document.getElementsByTagName("appnt");Node appnt = appntElement.item(0);// 得到第一个String name = appnt.getNodeName();// 得到元素的名称logger.debug("<appnt>元素的名称 : " + name);// appnt// 4-2 得到<appnt>元素所有子元素NodeList appntChildren = appnt.getChildNodes();// 4-3 得到<appnt>元素下<name>元素Node appnt_name = appntChildren.item(1);logger.debug("<appnt>元素下<name>子元素的名称 : " + appnt_name.getNodeName());logger.debug("<appnt>元素下<name>子元素的value : " + appnt_name.getTextContent());// 4-4 得到<appnt>元素下<age>元素// 中间有text类的内容也会被解析,所以这边1跳转到了3// item(2)的话,会输出#Text,是指在<appnt>#Text<age>中间的文本信息Node appnt_age = appntChildren.item(3);logger.debug("<appnt>元素下<age>子元素的名称 : " + appnt_age.getNodeName());logger.debug("<appnt>元素下<age>子元素的value : " + appnt_age.getTextContent());// 5-获取属性值Element insuredElement = (Element) document.getElementsByTagName("insured").item(0);String insuredName = insuredElement.getAttribute("name");String insuredAge = insuredElement.getAttribute("age");logger.debug("<insured>元素的name属性为: " + insuredName +" |age属性为" + insuredAge);// 6-添加节点元素// 6-1 新建元素Element element = document.createElement("新建标签");element.setAttribute("属性", "10块");element.setTextContent("我是动态新建的元素");// 6-2 放入根节点元素Element root = document.getDocumentElement();//获取根节点root.appendChild(element);//新建的元素放入根节点元素// 6-3 写入文件,需要使用 TransformerFactory 固定写法TransformerFactory tff = TransformerFactory.newInstance();Transformer transformer = tff.newTransformer();// 写出去DOMSource domSource = new DOMSource(document);Result result = new StreamResult(new FileOutputStream("resources/Request.xml"));transformer.transform(domSource, result);} catch (ParserConfigurationException e) {e.printStackTrace();} catch (SAXException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} catch (TransformerConfigurationException e) {e.printStackTrace();} catch (TransformerException e) {e.printStackTrace();}}
2、使用DOM解析整个XML文档,会使用递归方法。代码如下:
1)获取解析器等步骤都是一致的,这边使用文件流获取XML文件。
2)获取根节点后,使用递归获取根节点下的所有元素。方法为listChildrenNodes("根元素")
3)由于DOM解析的时候,会把空的文本信息Text也解析,我们可以预判断,处理掉。getChildNodes()可以得到子元素。
/** * Dom解析整个XML文档 * * @throws ParserConfigurationException * @throws SAXException * @throws IOException */public void domXML() throws ParserConfigurationException, SAXException, IOException {// 1-: 获得dom解析器工厂(工作的作用是用于创建具体的解析器)DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();// 2-:获得具体的dom解析器DocumentBuilder db = dbf.newDocumentBuilder();// 3-: 解析一个xml文档,获得Document对象InputStreamDocument document = db.parse(new File("resources/Request.xml"));// 4-得到根节点Element requestElement = document.getDocumentElement();logger.debug("得到根节点: " + requestElement.getNodeName());// 需要使用递归listChildrenNodes(requestElement);}/** * 递归方法 打印的时候会有每个Text对象,会处理掉 * * @param root */private void listChildrenNodes(Node root) {// 先判断一步, 把Text对象过滤掉if (root instanceof Element) {logger.debug("节点的名字为: " + root.getNodeName());}NodeList childrenLists = root.getChildNodes();for (int i = 0; i < childrenLists.getLength(); i++) {Node child = childrenLists.item(i);// 递归调用listChildrenNodes(child);}}
3、以上两个方法分别简单处理了jaxp的Dom解析XML的实例,在main方法中调用即可运行,全部代码为:
/** * 一、XML的解析方式有2中方式:(现在还有poi解析) * 1、DOM解析:dom对象树 * 1)整个XML放入内存,转换为Document对象; * 2)每个元素,转换成Element对象 * 3)文本,转换成Text对象 * 4)属性,转换成Attribute对象 * * 优缺点:元素的CRUD比较简单,但是内存消耗大,大的xml文档不适合。 * * * 2、SAX解析:从上往下读,读取一行,解析一行 * * 优缺点:不影响内存,但是不适合对文档作CRUD。 * * 二、XML解析开发包 * 1、jaxp * 2、jDom * 3、Dom4j * * * 三、调整JVM的内存大小 * 1、java.lang.OutOfMemoryError: Java heap space 堆内存不足 * 2、 固定为64M * 3、 -Xmx80m 修改VM Arguments * */package xml.code.base;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import javax.xml.parsers.DocumentBuilder;import javax.xml.parsers.DocumentBuilderFactory;import javax.xml.parsers.ParserConfigurationException;import javax.xml.transform.Result;import javax.xml.transform.Transformer;import javax.xml.transform.TransformerConfigurationException;import javax.xml.transform.TransformerException;import javax.xml.transform.TransformerFactory;import javax.xml.transform.dom.DOMSource;import javax.xml.transform.stream.StreamResult;import org.apache.log4j.Logger;import org.w3c.dom.Document;import org.w3c.dom.Element;import org.w3c.dom.Node;import org.w3c.dom.NodeList;import org.xml.sax.SAXException;/** * * DomJaxpXML.java * * @title Dom解析XML * @description * @author SAM-SHO * @Date 2014-10-12 */public class DomJaxpXML {private static Logger logger = Logger.getLogger(DomJaxpXML.class);public static void main(String[] args) throws Exception {// ************************调整JVM 内存大小// java.lang.OutOfMemoryError: Java heap space 堆内存不足// 固定为64M// -Xmx80m 修改VM Arguments// byte[] b = new byte[1024 * 1024 * 70];DomJaxpXML JavapXML = new DomJaxpXML();// 通过Dom获取XMLJavapXML.getXMLFromDom();JavapXML.domXML();}/** * 通过DOM获取xml文档 */public void getXMLFromDom() {try {// 1-: 获得dom解析器工厂(工作的作用是用于创建具体的解析器)DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();// 2-:获得具体的dom解析器DocumentBuilder db = dbf.newDocumentBuilder();// 3-: 解析一个xml文档,获得Document对象// 利用classloader加载xml文件,文件的位置很重要InputStream input = this.getClass().getClassLoader().getResourceAsStream("Request.xml");Document document = db.parse(input);// 4-通过元素名,得到某个元素// 4-1 得到<appnt>元素NodeList appntElement = document.getElementsByTagName("appnt");Node appnt = appntElement.item(0);// 得到第一个String name = appnt.getNodeName();// 得到元素的名称logger.debug("<appnt>元素的名称 : " + name);// appnt// 4-2 得到<appnt>元素所有子元素NodeList appntChildren = appnt.getChildNodes();// 4-3 得到<appnt>元素下<name>元素Node appnt_name = appntChildren.item(1);logger.debug("<appnt>元素下<name>子元素的名称 : " + appnt_name.getNodeName());logger.debug("<appnt>元素下<name>子元素的value : " + appnt_name.getTextContent());// 4-4 得到<appnt>元素下<age>元素// 中间有text类的内容也会被解析,所以这边1跳转到了3// item(2)的话,会输出#Text,是指在<appnt>#Text<age>中间的文本信息Node appnt_age = appntChildren.item(3);logger.debug("<appnt>元素下<age>子元素的名称 : " + appnt_age.getNodeName());logger.debug("<appnt>元素下<age>子元素的value : " + appnt_age.getTextContent());// 5-获取属性值Element insuredElement = (Element) document.getElementsByTagName("insured").item(0);String insuredName = insuredElement.getAttribute("name");String insuredAge = insuredElement.getAttribute("age");logger.debug("<insured>元素的name属性为: " + insuredName +" |age属性为" + insuredAge);// 6-添加节点元素// 6-1 新建元素Element element = document.createElement("新建标签");element.setAttribute("属性", "10块");element.setTextContent("我是动态新建的元素");// 6-2 放入根节点元素Element root = document.getDocumentElement();//获取根节点root.appendChild(element);//新建的元素放入根节点元素// 6-3 写入文件,需要使用 TransformerFactory 固定写法TransformerFactory tff = TransformerFactory.newInstance();Transformer transformer = tff.newTransformer();// 写出去DOMSource domSource = new DOMSource(document);Result result = new StreamResult(new FileOutputStream("resources/Request.xml"));transformer.transform(domSource, result);} catch (ParserConfigurationException e) {e.printStackTrace();} catch (SAXException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} catch (TransformerConfigurationException e) {e.printStackTrace();} catch (TransformerException e) {e.printStackTrace();}}/** * Dom解析整个XML文档 * * @throws ParserConfigurationException * @throws SAXException * @throws IOException */public void domXML() throws ParserConfigurationException, SAXException, IOException {// 1-: 获得dom解析器工厂(工作的作用是用于创建具体的解析器)DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();// 2-:获得具体的dom解析器DocumentBuilder db = dbf.newDocumentBuilder();// 3-: 解析一个xml文档,获得Document对象InputStreamDocument document = db.parse(new File("resources/Request.xml"));// 4-得到根节点Element requestElement = document.getDocumentElement();logger.debug("得到根节点: " + requestElement.getNodeName());// 需要使用递归listChildrenNodes(requestElement);}/** * 递归方法 打印的时候会有每个Text对象,会处理掉 * * @param root */private void listChildrenNodes(Node root) {// 先判断一步, 把Text对象过滤掉if (root instanceof Element) {logger.debug("节点的名字为: " + root.getNodeName());}NodeList childrenLists = root.getChildNodes();for (int i = 0; i < childrenLists.getLength(); i++) {Node child = childrenLists.item(i);// 递归调用listChildrenNodes(child);}}}
(二)SAX
1、 SAX采用事件处理的方式解析XML文件,涉及两个部分:解析器和事件处理器:
1)、解析器可以使用jaxp的API创建,创建出sax解析器后,就可以指定解析器去解析某个XML文档。
2)、解析器采用SAX方式在解析某个文档时,它只要解析到XML文档的一个组成部分都会去调用事件处理器的一个方法,解析器在调用事件处理器的方法时,会把当前解析到的XML内容作为方法参数传递给事件处理器。
3)、 事件处理器是由程序员编写的,程序员通过事件处理器中方法的参数,就可以很轻松地得到SAX解析器解析到的数据,从而可以决定如何对数据进行处理。
2、简单的理解:获取工厂、解析器、xml文档的步骤是统一的,我们只需要实现一个事件处理器,而且自定义的事件处理器是要继承DefaultHandler即可,不然会要实现接口的很多方法。
3、通过SAX解析整个XML文档:
1)这边我们没有的事件处理器没有继承DefaultHandler,采用的是实现ContentHandler,会实现很多,非常麻烦,不推荐。
2)获取工厂、得到解析器、得到读取器、设置内容处理器、解析xml文档的步骤是固定的,主要在于自己写的事件处理器。
3)这边设置读取器、内容处理器、解析XML是分开执行的,也可以一步到位,解析器类SAXParser有这样的封装。
4)事件处理器中需要用到的是三个方法:
①、startElement():获取元素的开始便签和属性。
②、characters():获取元素的文本内容。
③、endElement():获取元素的结束标签。
代码如下:
/** * 通过SAX解析整个XML */private void getSaxXML() {try {// 1-: 获得SAX解析器工厂(工作的作用是用于创建具体的解析器)SAXParserFactory spf = SAXParserFactory.newInstance();// 2-:获得具体的SAX解析器SAXParser tSAXParser = spf.newSAXParser();// 3-:得到读取器XMLReader xmlReader = tSAXParser.getXMLReader();// 4-设置内容处理器xmlReader.setContentHandler(new ListHandler());// 5-: 解析一个xml文档InputStream input = this.getClass().getClassLoader().getResourceAsStream("Request.xml");// 利用classloader加载xml文件xmlReader.parse(new InputSource(input));} catch (ParserConfigurationException e) {e.printStackTrace();} catch (SAXException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}
/** * * SaxJaxpXML.java * * @title 得到xml文档所有内容的Handler,继承自ContentHandler * @description * @author SAM-SHO * @Date 2014-10-20 */class ListHandler implements ContentHandler {private Logger logger = Logger.getLogger(ListHandler.class);@Overridepublic void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {// 开始标签logger.debug("<" + qName + ">");// 获取属性值for (int i = 0; atts != null && i < atts.getLength(); i++) {String attrName = atts.getQName(i);String attrValue = atts.getValue(attrName);logger.debug(attrName + "=" + attrValue + " ");}}@Overridepublic void characters(char[] ch, int start, int length) throws SAXException {// xml文档内容String content = new String(ch, start, length);logger.debug(content);}@Overridepublic void endElement(String uri, String localName, String qName) throws SAXException {// 结束标签logger.debug("</" + qName + ">");}@Overridepublic void endDocument() throws SAXException {// TODO Auto-generated method stub}@Overridepublic void endPrefixMapping(String prefix) throws SAXException {// TODO Auto-generated method stub}@Overridepublic void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {// TODO Auto-generated method stub}@Overridepublic void processingInstruction(String target, String data) throws SAXException {// TODO Auto-generated method stub}@Overridepublic void setDocumentLocator(Locator locator) {// TODO Auto-generated method stub}@Overridepublic void skippedEntity(String name) throws SAXException {// TODO Auto-generated method stub}@Overridepublic void startDocument() throws SAXException {// TODO Auto-generated method stub}@Overridepublic void startPrefixMapping(String prefix, String uri) throws SAXException {// TODO Auto-generated method stub}}
4、通过SAX获取指定标签的内容
1)我们利用SAXParser封装的方法,一步到位组装读取器、设置内容处理器、解析一个xml文档。
2)事件处理器直接继承DefaultHandler,然后重写需要的三个方法。
3)一个XML文件中相同名字的的元素可能会有多个,可以动态指定获取。
代码如下:
/** * 获取指定标签 */private void getTagXML() {try {// 1-: 获得SAX解析器工厂(工作的作用是用于创建具体的解析器)SAXParserFactory spf = SAXParserFactory.newInstance();// 2-:获得具体的SAX解析器SAXParser tSAXParser = spf.newSAXParser();// 3-: 解析一个xml文档InputStream input = this.getClass().getClassLoader().getResourceAsStream("Request.xml");// 利用classloader加载xml文件tSAXParser.parse(new InputSource(input), new TagValueHandler());// 直接一步到位} catch (ParserConfigurationException e) {e.printStackTrace();} catch (SAXException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}
/** * * SaxJaxpXML.java * * @title 获取指定标签的值,继承DefaultHandler * @description * @author SAM-SHO * @Date 2014-10-20 */class TagValueHandler extends DefaultHandler {private Logger logger = Logger.getLogger(TagValueHandler.class);private String currentTag; // 记住当前解析到的是什么标签private int needNumber = 2; // 记住想获取第几个作者便签的值,这边先定义为2private int currentNumber = 0; // 当前解析到的是第几个@Overridepublic void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {currentTag = qName;if (currentTag.equals("name")) {currentNumber++;}}@Overridepublic void characters(char[] ch, int start, int length) throws SAXException {// 当前解析到的便签为第二个<name>元素就输出if ("name".equals(currentTag) && currentNumber == needNumber) {String content = new String(ch, start, length);logger.debug(content);}}@Overridepublic void endElement(String uri, String localName, String qName) throws SAXException {currentTag = null;// 置空}}
- XML学习笔记(四):xml解析详解以及使用 DOM和SAX 解析XML :
- java学习笔记——使用DOM解析XML和使用SAX解析XML
- XML学习总结-DOM和SAX解析-综合案例-(四)
- XML以及XML解析方式[DOM+SAX]
- 解析xml:DOM 和 SAX
- XML解析方法(Pull和Sax以及DOM)
- XML 解析---dom解析和sax解析
- XML笔记_解析XML之DOM和SAX
- dom,sax解析xml
- XML-DOM SAX解析
- xml解析,dom/sax
- DOM 解析和SAX解析 XML 文档
- XML的SAX解析和DOM解析
- XML解析(SAX解析和DOM解析)
- iOS SAX 、DOM 、XML解析区别笔记
- JavaWeb-05 XML基础(Dom解析和Sax解析)
- XML文档的DOM和SAX解析方式详解
- 使用DOM,SAX解析XML文档
- Ubuntu 12.04LTS下配置OpenSSL和gmp环境
- 计算机网络基础
- 查看电脑硬件信息
- 【交互设计】移动端网页已死,用户切换的是内容
- win8.1下运行VC6.0
- XML学习笔记(四):xml解析详解以及使用 DOM和SAX 解析XML :
- JSP系列四:JSP9个内置对象
- [贪心+模拟] zoj 3829 Known Notation
- 【交互设计】移动端网页已死,用户切换的是内容
- 养花、赏花与养生五点关系
- Ioc容器-Autofac之一
- 题目1435:迷瘴 题目23
- 2015百度校园招聘笔试
- 放大电路三极管的三种连接方式