苦尽甘来 一个月学通JavaWeb(三 XML)

夜光序言:

 

和人打交道时,请牢记这一点--人并非理性生物。他们由情感驱使,被偏见支配,傲慢与虚荣是他们的动力之源~~~

苦尽甘来 一个月学通JavaWeb(三 XML)

苦尽甘来 一个月学通JavaWeb(三 XML)

 

正文:

、XML解析器介绍

 

操作XML文档概述

 

1 如何操作XML文档【夜光:面试一般都会玩这个骚操作,问你增删改查~~其实检不出多大水平】

XML文档也是数据的一种,对数据的操作也不外乎是“增删改查”。也被大家称之为“CRUD”。

  1. C:Create;
  2. R:Retrieve;
  3. U:Update;
  4. D:Delete

 

2 XML解析技术【百科】

XML解析方式分为两种:DOM(Document Object Model)和SAX(Simple API for XML)。这两种方式不是针对Java语言来解析XML的技术,而是跨语言的解析方式。例如DOM还在Javascript中存在!

DOM是W3C组织提供的解析XML文档的标准接口,而SAX是社区讨论的产物,是一种事实上的标准。

DOM和SAX只是定义了一些接口,以及某些接口的缺省实现,而这个缺省实现只是用空方法来实现接口。一个应用程序如果需要DOM或SAX来访问XML文档,还需要一个实现了DOM或SAX的解析器,也就是说这个解析器需要实现DOM或SAX中定义的接口。提供DOM或SAX中定义的功能。

 

解析原理【了解即可,一些琐碎的知识~~】

 

1 DOM解析原理

使用DOM要求解析器把整个XML文档装载到一个Document对象中。Document对象包含文档元素,即根元素,根元素包含N多个子元素…

一个XML文档解析后对应一个Document对象,这说明使用DOM解析XML文档方便使用,因为元素与元素之间还保存着结构关系。

优先:使用DOM,XML文档的结构在内存中依然清晰。元素与元素之间的关系保留了下来~~

缺点:如果XML文档过大,那么把整个XML文档装载进内存,可能会出现内存溢出的现象~~

 

2 设置Java最大内存

运行Java程序,指定初始内存大小,以及最大内存大小。

java -Xms20m -Xmx100m MyClass

3 SAX解析原理

DOM会一行一行的读取XML文档,最终会把XML文档所有数据存放到Document对象中。SAX也是一行一行的读取XML文档但是当XML文档读取结束后,SAX不会保存任何数据,同时整个解析XML文档的工作也就结束了。

但是,SAX在读取一行XML文档数据后,就会给感兴趣的用户一个通知!例如当SAX读取到一个元素的开始时,会通知用户当前解析到一个元素的开始标签。而用户可以在整个解析的过程中完成自己的业务逻辑,当SAX解析结束,不会保存任何XML文档的数据。

优先:使用SAX,不会占用大量内存来保存XML文档数据,效率也高。

缺点:当解析到一个元素时,上一个元素的信息已经丢弃,也就是说没有保存元素与元素之间的结构关系,这也大大限制了SAX的使用范围。如果只是想查询XML文档中的数据,那么使用SAX是最佳选择!

 

解析器概述

 

1 什么是XML解析器

DOM、SAX都是一组解析XML文档的规范,其实就是接口,这说明需要有实现者能使用,而解析器就是对DOM、SAX的实现了。一般解析器都会实现DOM、SAX两个规范!

  1. Crimson(sun):JDK1.4之前,Java使用的解析器。性能效差,可以忘记它了!
  2. Xerces(IBM):IBM开发的DOM、SAX解析器,现在已经由Apache基金会维护。是当前最为流行的解析器之一~~在1.5之后,已经添加到JDK之中,也是JAXP的默认使用解析器,但不过在JDK中的包名与Xerces不太一样。例如:org.apache.xerces包名改为了com.sun.org.apache.xerces.internal包名,也就是说JDK1.5中的Xerces是被包装后的XML解析器,但二者区别很小。
  3. Aelfred2(dom4j):DOM4J默认解析器,当DOM4J找不到解析器时会使用他自己的解析器。

 

JAXP概述

 

1 什么是JAXP

JAXP是由Java提供的,用于隐藏底层解析器的实现。Java要求XML解析器去实现JAXP提供的接口,这样可以让用户使用解析器时不依赖特定的XML解析器。

JAXP本身不是解析器(不是Xerces),也不是解析方式(DOM或SAX),它只是让用户在使用DOM或SAX解析器时不依赖特点的解析器。

当用户使用JAXP提供的方式来解析XML文档时,用户无需编写与特定解析器相关的代码,而是由JAXP通过特定的方式去查找解析器,来解析XML文档。

 

 

2 JAXP对DOM的支持【有点帅~~】【了解即可,不要背,知道就行了】

 

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

DocumentBuilder builder = factory.newDocumentBuilder();

Document doc = builder.parse("src/students.xml");

 

在javax.xml.parsers包中,定义了DOM解析器工厂类DocumentBuilderFactory,用于产生DOM解析器。DocumentBuilderFactory是一个抽象类,它有一个静态方法newInstance(),可以返回一个本类的实例对象。其实该方法返回的是DocumentBuilderFactory类的子类的实例(即工厂实例对象)。那么这个子类又是哪个子类呢?其实这个子类是由XML解析器提供商提供的,不同的厂商提供的工厂类对抽象工厂的实现是不同的。然后由工厂实例创建解析器对象。

那么newInstance()这个方法又是如果找到解析器提供商的工厂类的呢?此方法使用下面有序的查找过程来确定要加载的DocumentBuilderFactory实现类:

 

一、使用javax.xml.parsers.DocumentBuilderFactory系统属性。如果设置了这个系统属性的值,那么newInstance()方法就以这个属性的值来构造这个工厂的实例。通过下面的方法可以设置这个系统属性值。

System.setProperty(“javax.xml.parsers.DocumentBuilderFactory”, “工厂实现类名字”);

我们不建议大家用上面的方法来硬编码这个系统属性的值,如果这样设置,假如将来需要更换解析器,就必需修改代码。

二、如果你没有设置上面的系统属性,newInstance()方法就会采用下面的途径来查找抽象工厂的实现类。第二个途径在查找JRE下的lib子目录下的jaxp.properties文件。如果这个文件存在,那么就读取这个文件。我们可以在%JAVA_HOME%\jre\lib\目录下创建一个jaxp.properties文件。在这个文件中给出一个键值对。如下所示:

javax.xml.parsers.DocumentBuilderFactory=工厂实现类名字

这个key名字必须是javax.xml.parsers.DocumentBuilderFactory,而相对应的值也必须设置类路径。

三、如果通过前两种途径下没有找到工厂的实现类,那么就需要使用服务API。这个服务API实际上是查找一个JAR文件的META-INF\ services\ javax.xml.parsers.DocumentBuilderFactory这个文件(该文件无扩展名)。如果找到了这个文件,就以这个文件的内容做为工厂实现类。这种方式被大多数解析器提供商所采用。在它们发布的解析器JAR包中往往会找到上述文件。然后在这个文件当中指定自己解析器的工厂类的名字。我们只需要把这个JAR文件的路径写到类路径(classpath)中就可以了。但要注意的是,如果在你的classpath中有多个解析器的JAR包路径,这时以前面的类路径优先。

四、如果说在前三种途径中都没有找到工厂实现类,那么就使用平台缺省工厂实现类。

在JAXP的早期的版本(5.0以前)中,除了JAXP API外,还包含了一个叫做Crimson的解析器。从JAXP1.2开始,Sun公司对Apache的Xerces解析器重新包装了一下,并将org.apache.xerces包名改为了com.sun.org.apache.xerces.internal,然后在JAXP的开发包中一起提供,作为缺省的解析器。我们所使用的JDK1.5中包含的缺省解析器就是被重新包装过后的Xerces解析器。

 

  在获取到某个特定解析器厂商的DocumentBuilderFactory后,那么这个工厂对象创建出来的解析器对象当然就是自己厂商的解析器对象了。

 

3 JAXP对SAX的支持

 

SAXParserFactory factory = SAXParserFactory.newInstance();

SAXParser parser = factory.newSAXParser();

parser.parse("src/students.xml", new DefaultHandler() {

public void startDocument() throws SAXException {

System.out.println("解析开始");

}

 

public void endDocument() throws SAXException {

System.out.println("解析结束");

}

 

public void processingInstruction(String target, String data)

throws SAXException {

System.out.println("处理指令");

}

 

public void startElement(String uri, String localName, String qName,

Attributes atts) throws SAXException {

System.out.println("元素开始:" + qName);

}

 

public void characters(char[] ch, int start, int length)

throws SAXException {

System.out.println("文本内容:" + new String(ch, start, length).trim());

}

 

public void endElement(String uri, String localName, String qName)

throws SAXException {

System.out.println("元素结束:" + qName);

}

});

 

JAXP对SAX的支持与JAXP对DOM的支持是相同的~~~

 

JDOM和DOM4J

 

1 JDOM和DOM4J概述

JDOM和DOM4J都是针对Java解析XML设计的方式,它们与DOM相似。但DOM不是只针对Java,DOM是跨语言的,DOM在Javascript中也可以使用。而JDOM和DOM4J都是专业为Java而设计的,使用JDOM和DOM4J,对我们这些Java程序员而言会更加方便~~

 

2 JDOM和DOM4J比较

JDOM与DOM4J相比,DOM4J完胜~~

所以,我们应该在今后的开发中,把DOM4J视为首选。

在2000年,JDOM开发过程中,因为团队建议不同,分离出一支队伍,开发了DOM4J。DOM4J要比JDOM更加全面。

 

3 DOM4J查找解析器的过程

DOM4J首先会去通过JAXP的查找方法去查找解析器,如果找到解析器,那么就使用之;否则会使用自己的默认解析器Aelfred2。

DOM4J对DOM和SAX都提供了支持,可以把DOM解析后的Document对象转换成DOM4J的Document对象,当然了可以把DOM4J的Document对象转换成DOM的Document对象。

DOM4J使用SAX解析器把XML文档加载到内存,生成DOM对象。当然也支持事件驱动的方式来解析XML文档。

XML解析之JAXP(DOM)

 

JAXP获取解析器

 

1 JAXP相关包

JAXP相关开发包:javax.xml

DOM相关开发包:org.w3c.dom

SAX相关开发包:org.xml.sax

 

2 JAXP与DOM、SAX解析器的关系

JAXP只是作用只是为了让使用者不依赖某一特定DOM、SAX的解析器实现,当使用JAXP API时,使用者直接接触的就是JAXP API,而不用接触DOM、SAX的解析器实现API。

 

3 JAXP获取DOM解析器

  当我们需要解析XML文档时,首先需要通过JAXP API解析XML文档,获取Document对象。然后用户就需要使用DOM API来操作Document对象了。

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

factory.setValidating(false);

DocumentBuilder builder = factory.newDocumentBuilder();

Document doc = builder.parse("src/students.xml");

 

4 JAXP保存Document

当我们希望把Document保存到文件中去时,可以使用Transformer对象的transform()方法来完成。想获取Transformer对象,需要使用TransformerFactory对象。

与JAXP获取DOM解析器一样,隐藏了底层解析器的实现。也是通过抽象工厂来完成的,这里就不在赘述了。

    TransformerFactory tFactory = TransformerFactory.newInstance();

Transformer transformer = tFactory.newTransformer();

trans.setOutputProperty(OutputKeys.ENCODING, "UTF-8");

trans.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "students.dtd");

trans.setOutputProperty(OutputKeys.INDENT, "yes");

Source source = new DOMSource(doc);

Result result = new StreamResult(xmlName);

transformer.transform(source, result);

 

 

Transformer类的transform()方法的两个参数类型为:Source和Result,DOMSource是Source的实现类,StreamResult是Result的实现类。

 

5 JAXP创建Document

有时我们需要创建一个Document对象,而不是从XML文档解析而来。这需要使用DocumentBuider对象的newDocument()方法。

DocumentBuilderFactory factory = DocumentBuilderFactory

.newInstance();

DocumentBuilder builder = factory.newDocumentBuilder();

Document doc = builder.newDocument();

doc.setXmlVersion("1.0");

doc.setXmlStandalone(true);

 

 

5 学习DOM之前,先写两个方法

  1. Document getDocument(String xmlName):通过xmlName获取Document对象;
  2. void saveDocument(Document doc, String xmlName):保存doc到xmlName文件中。

 

DOM API概述

 

1 Document对应XML文档

无论使用什么DOM解析器,最终用户都需要获取到Document对象,一个Document对象对应整个XML文档。也可以这样说,Document对象就是XML文档在内存中的表示形式。

通常我们最为“关心”的就是文档的根元素。所以我们必须要把Document获取根元素的方法记住:Element getDocumentElement()。然后通过根元素再一步步获取XML文档中的数据。

 

2 DOM API中的类

  在DOM中提供了很多接口,用来描述XML文档中的组成部分。其中包括:文档(Document)、元素(Element)、属性(Attr)、文本(Text)、注释(Comment)、CDATA段(CDATASection)等等。无论是哪种XML文档组成部分,都是节点(Node)的子接口。

苦尽甘来 一个月学通JavaWeb(三 XML)

 

先缓解一下视觉疲劳~~

苦尽甘来 一个月学通JavaWeb(三 XML)

3 Node方法介绍【从不背,用到的时候看看~~】

 

Node基本方法:

  1. String getNodeName():获取当前节点的名字。如果当前节点是Element,那么返回元素名称。如果当前节点是Text那么返回#text。如果当前节点是Document那么返回#document;
  2. String getNodeValue():获取当前节点的值。只有文本节点有值,其它节点的值都为null;
  3. String getTextContext():获取当前节点的文本字符串。如果当前节点为Text,那么获取节点内容。如果当前节点为Element,那么获取元素中所有Text子节点的内容。例如当前节点为:<name>Genius</name>,那么本方法返回Genius。如果当前节点为:<student><name>Genius</name><age>23</age><sex>male</sex></student>,那么本方法返回Genius23male。
  4. short getNodeType():获取当前节点的类型。Node中有很多short类型的常量,可以通过与这些常量的比较来判断当前节点的类型。if(node.getNodeType() == Node.ELEMENT_NODE);

 

Node获取子节点和父节点方法,只有Document和Element才能使用这些方法:

  1. NodeList getChildNodes():获取当前节点的所有子节点。NodeList表示节点列表,它有两个方法:
  • int getLength():获取集合长度;
  • Node item(int index):获取指定下标的节点。
  1. Node getFirstNode():获取当前节点的第一个子节点;
  2. Node getLastNode():获取当前节点的最后一个子节点;
  3. Node getParentNode():获取当前节点的父节点。注意Document的父节点为null。

 

Node获取弟兄节点的方法,只有Element才能使用这些方法:

  1. Node getNextSibling():获取当前节点的下一个兄弟节点;
  2. Node getPreviousSibling():获取当前节点的上一个兄弟节点。

 

Node添加、替换、删除子节点方法

  1. Node appendChild(Node newChild):把参数节点newChild添加到当前节点的子节点列表的末尾处。返回值为被添加的子节点newChild对象,方便使用链式操作。如果newChild在添加之前已经在文档中存在,那么就是修改节点的位置了;
  2. Node insertBefore(Node newChild, Node refNode):把参数节点newChild添加到当前节点的子节点refNode之前。返回值为被添加的子节点newChild对象,方便使用链式操作。如果refNode为null,那么本方法与appendNode()方法功能相同。如果newChild节点在添加之前已经在文档中存在,那么就是修改节点的位置了。
  3. Node removeNode(Node oldChild):从当前节点中移除子元素oldChild。返回值为被添加的子节点oldChild对象,方便使用链式操作。
  4. Node replaceNode(Node newChild, Node oldChild):将当前节点的子节点oldChild替换为newChild。

 

Node获取属性集合方法,只有Element可以使用

  1. NamedNodeMap getAttributes():返回当前节点的属性集合。NamedNodeMap表示属性的集合,方法如下:
  • int getLength():获取集合中属性的个数;
  • Node item(int index):获取指定下标位置上的属性节点;
  • Node getNamedItem(String name):获取指定名字的属性节点;
  • Node removeNamedItem(String name):移除指定名字的属性节点,返回值为移除的属性节点;
  • Node setNamedItem(Node arg):添加一个属性节点,返回值为添加的属性节点。

 

Node的判断方法

  1. boolean hasChildNodes():判断当前节点是否有子节点;
  2. boolean hasAttribute():判断当前节点是否有属性。

 

4 Docment方法介绍

创建节点方法

  1. Attr createAttribute(String name):创建属性节点;
  2. CDATASection createCDATASection(String data):创建CDATA段节点;
  3. Comment createComment(String data):创建注释;
  4. Element createElement(String tagName):创建元素节点;
  5. Text createTextNode(String data):创建文本节点;

 

获取子元素方法

  1. Element getElementById(String elementId):通过元素的ID属性获取元素节点,如果没有DTD指定属性类型为ID,那么这个方法将返回null;
  2. NodeList getElementsByTagName(String tagName):获取指定元素名称的所有元素;
  3. Element getDocumentElement():获取文档元素,即获取根元素。

 

文档声明相关方法

  1. String getXmlVersion():获取文档声明的version属性值;
  2. String getXmlEncoding():获取文档声明的encoding属性值;
  3. String getXmlStandalone():获取文档声明的standalone属性值;
  4. void setXmlVersion():设置文档声明version属性值;
  5. void setXmlStandalone():设置文档声明standalone属性值。

 

5 Element方法介绍

获取方法

  1. NodeList getElementsByTagName(String tagName):获取当前元素的指定元素名称的所有子元素;
  2. String getTagName():获取当前元素的元素名。调用元素节点的getNodeName()也是返回名;

 

属性相关方法

  1. String getAttribute(String name):获取当前元素指定属性名的属性值;
  2. Attr getAttributeNode(String name):获取当前元素指定属性名的属性节点;
  3. boolean hasAttribute(String name):判断当前元素是否有指定属性;
  4. void removeAttribute(String name):移除当前元素的指定属性;
  5. void removeAttributeNode(Attr attr):移除当前元素的指定属性;
  6. void setAttribute(String name, String value):为当前元素添加或修改属性;
  7. Attr setAttributeNode(Attr attr):为当前元素添加或修改属性,返回值为添加的属性;

 

6 Attr方法介绍

  1. String getName():获取当前属性节点的属性名;
  2. String getValue():获取当前属性节点的属性值;
  3. void setValue(String value):设置当前属性节点的属性值;
  4. boolean isId():判断当前属性节点是否为ID类型属性。