@liayun
2016-06-29T07:48:08.000000Z
字数 15200
阅读 2361
JavaWeb
XML解析方式分为两种:dom和sax。
dom解析和sax解析的区别(面试题常考)
dom解析原理:
root.xml文档如下:
<?xml version="1.0"?><root><head><title>example</title></head><body><p>content1</p><p>content2</p></body><foot><author name="liayun" /></foot></root>
对其进行dom解析,如图:
如何调整JVM内存大小?(面试题也常考)
JVM最多允许你占用64M的内存,你所写的程序控制的内存超出64M,如果不调整JVM内存大小,根本就运行不起来。
例,
byte[] b = new byte[1024*1024*1024];
这句代码一执行,JVM就在内存里面开辟1G的空间,这时就会包内存溢出异常:java.lang.OutOfMemoryError: Java heap space。
调整JVM内存大小的步骤如图:
sax解析原理:
对以上文档进行sax解析,如图:
XML解析器
使用哪种解析器对程序员基本上没有什么影响,我们学习的是解析开发包,解析开发包调用什么样的解析器对程序员没有意义。
JAXP:Java API for xml Processing,jaxp是sun提供的一套xml解析API,jaxp很好地支持了dom和sax解析方式。
JAXP开发包是J2SE的一部分,它由javax.xml、org.w3c.dom 、org.xml.sax包及其子包组成。
在javax.xml.parsers包中,定义了几个工厂类,程序员调用这些工厂类,可以得到对xml文档进行解析的DOM或SAX的解析器对象。
javax.xml.parsers包中的DocumentBuilderFactory用于创建DOM模式的解析器对象,DocumentBuilderFactory是一个抽象工厂类,它不能直接实例化,但该类提供了一个newInstance方法,这个方法会根据本地平台默认安装的解析器,自动创建一个工厂的对象并返回。
调用DocumentBuilderFactory.newInstance()方法得到创建DOM解析器的工厂。
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
调用工厂对象的newDocumentBuilder方法得到DOM解析器对象。
DocumentBuilder builder = factory.newDocumentBuilder();
调用DOM解析器对象的parse()方法解析XML文档,得到代表整个文档的Document对象,进而可以利用DOM特性对整个XML文档进行操作了。
Document document = builder.parse("src/book.xml");
DOM模型(document object model)
DOM解析器在解析XML文档时,会把文档中的所有元素,按照其出现的层次关系,解析成一个个Node对象(节点),在dom中,节点之间关系如下:
节点类型:
| 类型名 | 说明 |
|---|---|
| Node | 表示所有类型值的统一接口,此接口中提供了很多增删改查节点的方法,所有的文档树中的对象都实现过此接口 |
| Document | 表示文档类型 |
| Element | 表示元素节点类型 |
| Text | 表示文本节点类型 |
| CDATASection | 表示CDATA区域类型 |
| DocumentType | 表示文档声明类型 |
| DocumentFragment | 表示文档片段类型 |
| Attr | 表示属性节点类型 |
Node对象
注意:dom解析下,xml文档的每一个组成部分都会用一个对象表示,例如标签用Element,属性用Attr,但不管什么对象,都是Node的子类,所以在开发中可以把获取到的任意节点都当作Node对待。
例如,有book.xml文档如下:
<?xml version="1.0" encoding="UTF-8" standalone="no"?><书架><书><书名 name="xxxxx">Java就业培训教程</书名><作者>张孝祥</作者><售价>39.00元</售价></书><书><书名>JavaScript网页开发</书名><作者>张孝祥</作者><售价>28.00元</售价></书></书架>
使用dom方式对xml文档进行crud
读取book.xml文档中书名元素(标签)<书名>JavaScript网页开发</书名>中封装的内容。
public void read1() throws Exception {DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = factory.newDocumentBuilder();Document document = builder.parse("src/book.xml");NodeList list = document.getElementsByTagName("书名"); // 获取到书名元素(标签)Node node = list.item(1);String content = node.getTextContent(); // 获取到书名元素(标签)中封装的内容System.out.println(content); // 输出:JavaScript网页开发}
得到xml文档中的所有标签。须使用递归,可断点调试跟踪,理解其原理。
NodeList getChildNodes():包含此节点的所有子节点的NodeList。如果不存在子节点,则这是不包含节点的NodeList。
public void read2() throws Exception {DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = factory.newDocumentBuilder();Document document = builder.parse("src/book.xml");// 得到根节点Node root = document.getElementsByTagName("书架").item(0);list(root);}private void list(Node node) {if(node instanceof Element)System.out.println(node.getNodeName());NodeList list = node.getChildNodes();for(int i = 0; i < list.getLength(); i++) {Node child = list.item(i);list(child);}}
千万注意:对于XML标签中出现的所有空格和换行,XML解析程序都会当作标签内容进行处理。
得到xml文档中标签<书名 name="xxxx">Java就业培训教程</书名>属性的值。
public void read3() throws Exception {DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = factory.newDocumentBuilder();Document document = builder.parse("src/book.xml");Element bookName = (Element) document.getElementsByTagName("书名").item(0);String value = bookName.getAttribute("name");System.out.println(value);}
向xml文档中添加节点:<售价>59.00元</售价>。
获得JAXP中的DOM解析器,调用DOM解析器对象的parse()方法解析XML文档,得到代表整个文档的Document对象。
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = factory.newDocumentBuilder();Document document = builder.parse("src/book.xml");
创建节点。
Element price = document.createElement("售价");price.setTextContent("59.00元");
把创建的节点挂到第一本书上。
Element book = (Element) document.getElementsByTagName("书").item(0);book.appendChild(price);
更新XML文档,把更新后的内存树写回到xml文档。
javax.xml.transform包中的Transformer类用于把代表XML文件的Document对象转换为某种格式后进行输出,例如把xml文件应用样式表后转成一个html文档。利用这个对象,当然也可以把Document对象又重新写入到一个XML文件中。Transformer对象通过TransformerFactory获得。
Transformer类通过transform方法完成转换操作,该方法接收一个源和一个目的地,我们可以通过:
TransformerFactory tfFactory = TransformerFactory.newInstance();Transformer tf = tfFactory.newTransformer();tf.transform(new DOMSource(document), new StreamResult(new FileOutputStream("src/book.xml")));
添加后book.xml内容:
<?xml version="1.0" encoding="UTF-8" standalone="no"?><书架><书><书名 name="xxxxx">Java就业培训教程</书名><作者>张孝祥</作者><售价>39.00元</售价><售价>59.00元</售价></书><书><书名>JavaScript网页开发</书名><作者>张孝祥</作者><售价>28.00元</售价></书></书架>
向xml文档中指定位置上添加节点:<售价>59.00元</售价>
获得JAXP中的DOM解析器,调用DOM解析器对象的parse()方法解析XML文档,得到代表整个文档的Document对象。
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = factory.newDocumentBuilder();Document document = builder.parse("src/book.xml");
创建节点。
Element price = document.createElement("售价");price.setTextContent("59.00元");
得到参考节点。
Element refNode = (Element) document.getElementsByTagName("售价").item(0);
得到要挂崽的节点。
Element book = (Element) document.getElementsByTagName("书").item(0);
往book节点的指定位置插崽。
book.insertBefore(price, refNode);
把更新后的内存树写回到xml文档。
TransformerFactory tfFactory = TransformerFactory.newInstance();Transformer tf = tfFactory.newTransformer();tf.transform(new DOMSource(document), new StreamResult(new FileOutputStream("src/book.xml")));
添加后book.xml内容:
<?xml version="1.0" encoding="UTF-8" standalone="no"?><书架><书><书名 name="xxxxx">Java就业培训教程</书名><作者>张孝祥</作者><售价>59.00元</售价><售价>39.00元</售价></书><书><书名>JavaScript网页开发</书名><作者>张孝祥</作者><售价>28.00元</售价></书></书架>
向xml文档节点上添加属性:<书名>Java就业培训教程</书名>上添加name="xxxxx"属性。为了进行以下代码的测试,先把原book.xml文档中<书名>Java就业培训教程</书名>元素中的属性name删除掉。
public void addAttr() throws Exception {DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = factory.newDocumentBuilder();Document document = builder.parse("src/book.xml");Element bookName = (Element) document.getElementsByTagName("书名").item(0);bookName.setAttribute("name", "xxxxx");// 把更新后的内存树写回到xml文档TransformerFactory tfFactory = TransformerFactory.newInstance();Transformer tf = tfFactory.newTransformer();tf.transform(new DOMSource(document), new StreamResult(new FileOutputStream("src/book.xml")));}
添加name="xxxxx"属性之后的book.xml内容:
<?xml version="1.0" encoding="UTF-8" standalone="no"?><书架><书><书名 name="xxxxx">Java就业培训教程</书名><作者>张孝祥</作者><售价>39.00元</售价></书><书><书名>JavaScript网页开发</书名><作者>张孝祥</作者><售价>28.00元</售价></书></书架>
注意:以上方式添加的元素都没有格式上的缩进。
例,book.xml文档修改为如下:
<?xml version="1.0" encoding="UTF-8" standalone="no"?><书架><书><书名 name="xxxxx">Java就业培训教程</书名><作者>张孝祥</作者><售价>59.00元</售价><售价>39.00元</售价></书><书><书名>JavaScript网页开发</书名><作者>张孝祥</作者><售价>28.00元</售价></书></书架>
删除xml文档中的标签<售价>59.00元</售价>。
获得JAXP中的DOM解析器,调用DOM解析器对象的parse()方法解析XML文档,得到代表整个文档的Document对象。
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = factory.newDocumentBuilder();Document document = builder.parse("src/book.xml");
得到要删除的节点。
Element e = (Element) document.getElementsByTagName("售价").item(0);
删除节点有两种方式:
第一种方式:
得到要删除的节点的爸爸。
Element book = (Element) document.getElementsByTagName("书").item(0);
爸爸再删崽。
book.removeChild(e);
第二种方式:
得到要删除的节点的爸爸之后再删崽。
e.getParentNode().removeChild(e);
在做开发时,有可能要删除节点所在的节点。
e.getParentNode().getParentNode().removeChild(e.getParentNode());
删除整个xml文档(整个xml文档的爸爸是document)。
e.getParentNode().getParentNode().getParentNode().removeChild(e.getParentNode().getParentNode());
把更新后的内存树写回到xml文档。
TransformerFactory tfFactory = TransformerFactory.newInstance();Transformer tf = tfFactory.newTransformer();tf.transform(new DOMSource(document), new StreamResult(new FileOutputStream("src/book.xml")));
删除节点之后book.xml内容为:
<?xml version="1.0" encoding="UTF-8" standalone="no"?><书架><书><书名 name="xxxxx">Java就业培训教程</书名><作者>张孝祥</作者><售价>39.00元</售价></书><书><书名>JavaScript网页开发</书名><作者>张孝祥</作者><售价>28.00元</售价></书></书架>
更新节点<售价>59.00元</售价>中的售价。
public void update() throws Exception {DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = factory.newDocumentBuilder();Document document = builder.parse("src/book.xml");Element e = (Element) document.getElementsByTagName("售价").item(0);e.setTextContent("109元");// 把更新后的内存树写回到xml文档TransformerFactory tfFactory = TransformerFactory.newInstance();Transformer tf = tfFactory.newTransformer();tf.transform(new DOMSource(document), new StreamResult(new FileOutputStream("src/book.xml")));}
修改之后book.xml文档的内容为:
<?xml version="1.0" encoding="UTF-8" standalone="no"?><书架><书><书名 name="xxxxx">Java就业培训教程</书名><作者>张孝祥</作者><售价>109元</售价><售价>39.00元</售价></书><书><书名>JavaScript网页开发</书名><作者>张孝祥</作者><售价>28.00元</售价></书></书架>
SAX采用事件处理的方式解析XML文件,利用SAX解析XML文档,涉及两个部分:解析器和事件处理器。
阅读ContentHandler API文档,常用方法有:startElement、endElement、characters。
例,有book.xml文档如下:
<?xml version="1.0" encoding="UTF-8"?><书架><书><书名>Java就业培训教程</书名><作者>张孝祥</作者><售价>109元</售价></书><书><书名 name="xxxx">JavaScript网页开发</书名><作者>黎活明</作者><售价>28.00元</售价></书></书架>
SAX方式解析XML文档的步骤:
使用SAXParserFactory创建SAX解析工厂。
SAXParserFactory factory = SAXParserFactory.newInstance();
通过SAX解析工厂得到解析器对象。
SAXParser sp = factory.newSAXParser();
通过解析器对象得到一个XML的读取器。
XMLReader reader = sp.getXMLReader();
设置读取器的事件处理器,一般是内容处理器,即ContentHandler。
XMLReader reader = sp.getXMLReader();
解析xml文件。
reader.parse("src/book.xml");
ContentHandler是一个接口,我们可以自己写一个类实现这个接口,其中提供了如下重要的方法:
void characters(char[] ch, int start, int length):接收字符数据的通知。endDocument():接收文档的结尾的通知。startDocument():接收文档的开始的通知。startElement(String uri, String localName, String qName, Attributes atts):接收元素开始的通知。void endElement(String uri, String localName, String qName):接收元素结束的通知。
编写一个类ListHandler实现ContentHandler接口,其作用是得到xml文档所有内容。
class ListHandler implements ContentHandler {@Overridepublic void setDocumentLocator(Locator locator) {// TODO Auto-generated method stub}@Overridepublic void startDocument() throws SAXException {// TODO Auto-generated method stub}@Overridepublic void endDocument() throws SAXException {// TODO Auto-generated method stub}@Overridepublic void startPrefixMapping(String prefix, String uri) throws SAXException {// TODO Auto-generated method stub}@Overridepublic void endPrefixMapping(String prefix) throws SAXException {// TODO Auto-generated method stub}/** 解析器只要解析到开始标签,就会调用startElement()方法* 会通过方法的参数告诉你解析到的当前标签名称是什么*/@Overridepublic void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {System.out.println("<"+qName+">");for(int i = 0; atts != null && i < atts.getLength(); i++) { // 有可能抛空指针异常,代码写的越紧凑越好String attName = atts.getQName(i);String attValue = atts.getValue(i);System.out.println(attName+"="+attValue);}}@Overridepublic void endElement(String uri, String localName, String qName) throws SAXException {System.out.println("<"+qName+">");}@Overridepublic void characters(char[] ch, int start, int length) throws SAXException {System.out.println(new String(ch, start, length));}@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 skippedEntity(String name) throws SAXException {// TODO Auto-generated method stub}}
可发现ContentHandler接口中的方法都要一一实现,未免麻烦。注意DefaultHandler类,该类实现了某些处理xml文档必须的接口——ContentHandler,但是均没有具体的方法,也就是说是空方法,如果想要解析xml文档,需要覆写该方法。
编写一个类TagValueHandler实现DefaultHandler类,其作用是获取指定标签的值:<作者>张孝祥</作者>。
class TagValueHandler extends DefaultHandler {private String currentTag; // 记住当前解析到的是什么标签private int needNumber = 1; // 记住想获取第几个作者标签的值private int currentNumber; // 当前解析到的是第几个@Overridepublic void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {currentTag = qName;if(currentTag.equals("作者")) {currentNumber++;}}@Overridepublic void endElement(String uri, String localName, String qName) throws SAXException {currentTag = null;}@Overridepublic void characters(char[] ch, int start, int length) throws SAXException {if("作者".equals(currentTag) && currentNumber == needNumber) {System.out.println(new String(ch, start, length));}}}
在实际开发中,我们是要把xml文档中的每一本书封装到一个book对象中,并把多个book对象放在一个List集合中返回。
先将封装数据的JavaBean,即Book.java设计出来:
public class Book {private String name;private String author;private String price;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}public String getPrice() {return price;}public void setPrice(String price) {this.price = price;}}
再编写一个类BeanListHandler实现DefaultHandler类,把xml文档中的每一本书封装到一个book对象中,并把多个book对象放在一个List集合中返回。
class BeanListHandler extends DefaultHandler {private List list = new ArrayList();private String currentTag;private Book book;@Overridepublic void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {currentTag = qName;if("书".equals(currentTag)) {book = new Book();}}@Overridepublic void characters(char[] ch, int start, int length) throws SAXException {if("书名".equals(currentTag)) { // currentTag放在前面同样有问题String name = new String(ch, start, length);book.setName(name);}if("作者".equals(currentTag)) {String author = new String(ch, start, length);book.setAuthor(author);}if("售价".equals(currentTag)) {String price = new String(ch, start, length);book.setPrice(price);}}@Overridepublic void endElement(String uri, String localName, String qName) throws SAXException {if(qName.equals("书")) {list.add(book);book = null;}currentTag = null; //不置空不行}public List getBooks() {return list;}}
BeanListHandler类中要注意两点:
endElement(String uri, String localName, String qName)方法中currentTag变量一定要置为空,不置为空绝对不行,会报异常java.lang.NullPointerException,若要知道其原因,可断点跟踪sax解析案例,分析程序问题,可发现其原因就是对于XML标签中出现的所有空格和换行,XML解析程序都会当作标签内容进行处理。characters(char[] ch, int start, int length)方法中,currentTag依次和标签名判断时,一定要放在后面,currentTag放在前面同样有问题。最后,在main方法中测试:
public class Demo {public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {// 1.创建解析工厂SAXParserFactory factory = SAXParserFactory.newInstance();// 2.得到解析器SAXParser sp = factory.newSAXParser();// 3.得到读取器XMLReader reader = sp.getXMLReader();// 4.设置内容处理器BeanListHandler handler = new BeanListHandler();reader.setContentHandler(handler);// 5.读取xml文档内容reader.parse("src/book.xml");@SuppressWarnings("unchecked")List<Book> list = handler.getBooks();System.out.println(list);}class BeanListHandler extends DefaultHandler {...}}
这里我参考: