Java解析XML
1.1JavaXML解析器
解析XML是指将通过XML文档访问数据或修改数据的一个操作或方法。
**Java库中提供了两种XML解析器:**
1、像文档对象模型(Document Object Model,DOM)解析器这的树型解析器(tree parse),它们将读入的XML文档转换 成树结构。
2、像XML简单API(Simple API for XML,SAX)解析器这样的流机制解析器(streaming parser),它们在读入XML文档时生 成相应的事件。
#### 1.2、XML解析器是什么?
XML解析器提供方法来访问或修改XML文档中的数据。 Java提供了多种选择来解析XML文档。以下是各种类型解析器其通常用于解析XML文档。
**Dom解析器 -** 解析通过加载该文件的全部内容,并创建其完整分级树中存储的文件。
**SAX解析器 -** 解析基于事件触发器的文档。不完整(部分)的文件加载到存储器中。
**JDOM解析器 - **解析以类似的方式,以DOM解析器但更简单的方法的文档。
**StAX解析器 -** 解析以类似的方式,以SAX解析器但在更高效的方式的文档。
**XPath解析器 - **解析基于表达式XML并广泛选择使用XSLT。
**DOM4J解析器 - **Java库来解析XML,XPath和使用Java集合框架XSLT,为DOM,SAX和JAXP的支持。
2、Java DOM解析器
#### 2.1、DOM解析器简介
文档对象模型是万维网联盟(W3C)的官方推荐。它定义了一个接口,使程序能够访问和更新样式,结构和XML文档的内容。支持DOM实现该接口的XML解析器。
**何时使用?**
在以下几种情况时,应该使用DOM解析器:
1、需要知道很多关于文档的结构
2、需要将文档的部分周围(例如,可能需要某些元素进行排序)
3、需要使用的文件中的信息超过一次
**会得到什么?**
当使用DOM 解析器解析一个XML文档,会得到一个树形结构,其中包含的所有文档的元素。 DOM提供了多种可用于检查文档的内容和结构的函数。
**优势**
DOM是用于处理文档结构的通用接口。它的一个设计目标是Java代码编写一个DOM兼容的解析器,运行在任何其他的DOM兼容的解析器不会有变化。
**DOM接口**
DOM定义了几个Java接口。这里是最常见的接口:
1、节点 - DOM的基本数据类型。
2、元素 - 要处理的对象绝大多数是元素。
3、Attr - 代表元素的属性。
4、文本 - 元素或Attr的实际内容。
5、文档 - 代表整个XML文档。文档对象是通常被称为DOM树。
**常见的DOM方法**
当正在使用DOM,有经常用到的几种方法:
1、Document.getDocumentElement() - 返回文档的根元素。
2、Node.getFirstChild() - 返回给定节点的第一个子节点。
3、Node.getLastChild() - 返回给定节点的最后一个子节点。
4、Node.getNextSibling() - 这些方法返回一个特定节点的下一个兄弟节点。
5、Node.getPreviousSibling() - 这些方法返回一个特定节点的前一个兄弟节点。
6、Node.getAttribute(attrName) - 对于给定的节点,则返回所请求的名字的属性。
#### 2.2、Java DOM解析器 - 解析XML文档
**使用DOM的步骤**
以下是在使用DOM解析器解析文档使用的步骤。
1、导入XML相关的软件包。
2、创建DocumentBuilder
3、从文件或流创建一个文档
4、提取根元素
5、检查属性
6、检查子元素
**document对象:**
要操作XML,先就得有Document对象,把一个XML文件加载进内存的时候,在内存中形成所谓的一种树状结构,我们把这一个结构称之为DOM树.
**注意:**
我们在Java代码中所做的增/删/改/查操作,都仅仅是操作的是内存中的Document对象,和磁盘中的XML文件没有关系。比如:删除一个联系人信息之后,XML文件中数据依然存在,此时出现内存中的数据和磁盘文件中的数据不同步。所以,对于增删改操作,我们需要做同步操作(把内存中的数据和磁盘的XML文件数据保持一致)。
DOM:在第一次的时候就会把XML文件加载进内存,如果XML文件过大,可能会造成内存的溢出.
DOM:在做增删改查操作的时候比较简单,,但是性能却不高(线性搜索).
**获取Document对象:**
**演示示例:**
步骤1:创建 ’Java-DOM解析器‘ 工程,并且在resources目录下创建domDemo.xml文件
```xml
<?xml version="1.0" encoding="utf-8" ?>
<class>
<student rollno="393">
<firstname>dinkar</firstname>
<lastname>kad</lastname>
<nickname>dinkar</nickname>
<marks>85</marks>
</student>
<student rollno="493">
<firstname>Vaneet</firstname>
<lastname>Gupta</lastname>
<nickname>vinni</nickname>
<marks>95</marks>
</student>
<student rollno="593">
<firstname>jasvir</firstname>
<lastname>singn</lastname>
<nickname>jazz</nickname>
<marks>90</marks>
</student>
</class>
```
步骤2:创建dom解析类 ‘com.zpark.DOMParseDemo’
```java
package com.zpark;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
public class DOMParseDemo {
public static void main(String[] args) throws Exception {
/*
1):表示出需要被操作的XML文件的路径,注意是文件的路径,不是文件所在的目录.
File f = new File(...);
2):根据DocumentBuilderFactory类,来获取DocumentBuilderFactory对象.
注意:工厂设计模式往往体现着单例设计模式.
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance() ;
3):根据DocumentBuilderFactory对象,构建DocumentBuilder对象.
注意:XxxFactory,就是用来创建Xxx对象的.
DocumentBuilder build = factory .newDocumentBuilder();
4):根据DocumentBuidler对象,构建Document对象.
Document doc = build.parse(f);
*/
// 获取xml文件路径
String path = DOMParseDemo.class.getClassLoader().getResource("domDemo.xml").getPath();
// 由于路径中包含中文,所以需要对路径解析解码
path = URLDecoder.decode(path, "utf-8");
File file = new File(path);
// 获取DocumentBuilderFactory对象
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
// 根据DocumentBuilderFactory对象,构建DocumentBuilder对象
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
// 根据DocumentBuidler对象,构建Document对象
Document document = documentBuilder.parse(path);
// 获取给定标签名元素内容
NodeList student = document.getElementsByTagName("student");
for (int i = 0; i < student.getLength(); i++) {
// 获取节点对象
Node node = student.item(i);
// 将node转为element,node为element父接口
Element element = (Element)node;
// 根据标签名字获取标签的text值
String firstname = element.getElementsByTagName("firstname").item(0).getTextContent();
System.out.println(firstname);
}
}
}
```
3、Java SAX解析器
#### 3.1、Java SAX解析器简介
SAX解析器在解析XML输入数据的各个组成部分时会报告事件,但不会以任何方式存储文档,而是由事件处理器建立相应的数据结构。实际上DOM解析器是在SAX解析器的基础上构建的,它在接收到解析器事件时构建DOM树。
SAX(针对XML的简单API)是基于事件为XML文档的解析器。不像DOM解析器,SAX解析器创建没有解析树。 SAX是一个流接口用于XML的,这意味着使用SAX应用接收事件通知有关XML文档被处理的元素,属性,在按顺序每次开始在文档的顶部,并与所述闭合结束根元素。
1、读取XML文件从上到下,构成一个结构完整的XML文档的标记
2、令牌以相同的顺序进行处理,它们出现在文档中
3、报告应用程序,因为它们所出现解析器遇到标记的特性
4、应用程序提供了必须的解析器注册的“事件”处理程序
5、作为标记标识,在处理程序回调方法相关信息调用
**什么时候使用?**
应该使用SAX解析器的时候:
1、可以在XML文档从上往下处理以线性方式
2、该文件并不深层次嵌套
3、处理一个非常大的XML文档,DOM树会占用太多的内存。典型DOM的实现使用10字节的存储器以表示XML的一个字节
4、解决的问题涉及的XML文档的一部分
5、数据是可用的,只要它是由解析器看出,这样的SAX可以很好地用于到达流的XML文档
**SAX的缺点**
1、它是在一个只进入处理随机访问方式XML文档
2、如果需要跟踪的数据分析器已经看到或更改项目的顺序,必须自已编写代码和数据存储
**ContentHandler接口**
此接口指定SAX解析器用来通知XML文档,已经看到部件应用程序的回调方法。
| 方法 | 方法描述 |
| ------------------------------------------------------------ | ------------------------------------ |
| void startDocument() | 调用在一个文件的开头。 |
| void endDocument() | 调用在一个文件的末尾。 |
| void startElement(String uri, String localName, String qName, Attributes atts) | 调用在一个元素的开头 |
| void endElement(String uri, String localName,String qName) | 调用在一个元件的末端。 |
| void characters(char[] ch, int start, int length) | 字符数据出现时调用。 |
| void ignorableWhitespace( char[] ch, int start, int length) | 当DTD是当前和忽略空白遇到时调用。 |
| void processingInstruction(String target, String data) | 当处理指令的认可时调用。 |
| void setDocumentLocator(Locator locator)) | 提供可用于识别文档中的位置的定位器。 |
| void skippedEntity(String name) | 一个尚未解决实体遇到时调用。 |
| void startPrefixMapping(String prefix, String uri) | 当一个新的命名空间的映射定义调用。 |
| void endPrefixMapping(String prefix) | 当一个命名空间定义结束其范围时调用。 |
**属性接口**
这种接口指定用于处理连接到一个元素的属性的方法。
int getLength() - 返回属性的数目。
String getQName(int index)
String getValue(int index)
String getValue(String qname)
3.2、Java SAX解析器 - 解析XML文档
在使用SAX解析器时,需要一个处理器来为各种解析器事件定义事件动作。DefaultHandler接口定义了若干个在解析文档时解析器会调用的回调方法。下面是最重要的几个方法:
1、startElement和endElement在每当遇到起始或终止标签时调用。
2、characters在每当遇到字符数据时调用。
3、startDocument和endDocument分别在文档开始和结束时各调用一次。
例如,在解析以下片段时:
```xml
<person>
<name type="string">韩信</name>
<age>25</age>
</person>
```
解析器会产生以下回调:
1)startElement,元素名:person
2)startElement,元素名:name ,属性:type="string"
3)characters,内容:韩信
4)endElement,元素名:name
5)startElement,元素名:age
6)characters,内容:25
7)endElement,元素名:age
8)endElement,元素名:person
处理器必须覆盖这些方法,让它们执行在解析文件时我们想让它们执行的动作。下面通过一个简单的demo体会SAX解析XML的过程。
**准备xml**
首先准备person.xml,内容如下
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<persons>
<person>
<name>韩信</name>
<age>25</age>
</person>
<person>
<name>李白</name>
<age>23</age>
</person>
</persons>
```
**解析代码**
1、获取解析工厂
2、从解析工厂获取解析器
3、得到解读器
4、设置内容处理器
5、读取xml的文档内容
```java
package com.zpark;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.net.URLDecoder;
/**
* @PackageName: com.zpark
* @ClassName: SAXParseFactoryDemo
* @Description: SAX方式解析xml文件
* @author: RZS
* @date: 2021/8/15 15:37
*/
public class SAXParseFactoryDemo {
public static void main(String[] args) throws Exception{
// 获取xml文件路径
String path = SAXParseFactoryDemo.class.getClassLoader().getResource("sax-parse.xml").getPath();
path = URLDecoder.decode(path, "utf-8");
// SAX解析
// 1.获取解析工厂
SAXParserFactory factory = SAXParserFactory.newInstance();
// 2.从解析工厂获取解析器
SAXParser parse = factory.newSAXParser();
// 3.得到解读器
XMLReader reader=parse.getXMLReader();
// 4.设置内容处理器
reader.setContentHandler(new PHandler());
// 5.读取xml的文档内容
reader.parse(path);
}
}
class PHandler extends DefaultHandler {
/**
* @author lastwhisper
* @desc 文档解析开始时调用,该方法只会调用一次
* @param
* @return void
*/
@Override
public void startDocument() throws SAXException {
System.out.println("----解析文档开始----");
}
/**
* @author lastwhisper
* @desc 每当遇到起始标签时调用
* @param uri xml文档的命名空间
* @param localName 标签的名字
* @param qName 带命名空间的标签的名字
* @param attributes 标签的属性集
* @return void
*/
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
System.out.println("标签<"+qName + ">解析开始");
}
/**
* @author lastwhisper
* @desc 解析标签内的内容的时候调用
* @param ch 当前读取到的TextNode(文本节点)的字节数组
* @param start 字节开始的位置,为0则读取全部
* @param length 当前TextNode的长度
* @return void
*/
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
String contents = new String(ch, start, length).trim();
if (contents.length() > 0) {
System.out.println("内容为-->" + contents);
} else {
System.out.println("内容为-->" + "空");
}
}
/**
* @author lastwhisper
* @desc 每当遇到结束标签时调用
* @param uri xml文档的命名空间
* @param localName 标签的名字
* @param qName 带命名空间的标签的名字
* @return void
*/
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
System.out.println("标签</"+qName + ">解析结束");
}
/**
* @author lastwhisper
* @desc 文档解析结束后调用,该方法只会调用一次
* @param
* @return void
*/
@Override
public void endDocument() throws SAXException {
System.out.println("----解析文档结束----");
}
**分析解析过程**
```text
----解析文档开始---- 调用startDocument
标签<persons>解析开始 调用startElement
内容为-->空 调用characters,因为<persons></persons>之间只有标签没有内容,所以为空。
标签<person>解析开始 调用startElement
内容为-->空 调用characters,因为<person></person>之间只有标签没有内容,所以为空。
标签<name>解析开始 调用startElement
内容为-->韩信 调用characters,<name>韩信</name>之间内容为:韩信
标签</name>解析结束 调用endElement
内容为-->空 调用characters,每次执行完调用endElement之后会调用一次characters
标签<age>解析开始 调用startElement
内容为-->25 调用characters,<age>25</age>之间内容为:25
标签</age>解析结束 调用endElement
内容为-->空 调用characters,每次执行完调用endElement之后会调用一次characters
标签</person>解析结束 调用endElement
内容为-->空 调用characters,每次执行完调用endElement之后会调用一次characters
...
标签<person>解析开始
内容为-->空
标签<name>解析开始
内容为-->李白
标签</name>解析结束
内容为-->空
标签<age>解析开始
内容为-->23
标签</age>解析结束
内容为-->空
标签</person>解析结束
内容为-->空
标签</persons>解析结束
----解析文档结束---- 调用endDocument,xml解析结束
```
课堂实例:
首先dir目录创建一个 beans.xml文件,写入数据
创建类来解析xml文档代码如下:
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class Demo03 {
public static void main(String[] args) throws Exception {
// 获取xml路径
String path = "./dir/SqlMapConfig.xml";
// 创建输入流对象
InputStream in = new FileInputStream(path);
// 创建SAXReader对象
SAXReader reader = new SAXReader();
// 生成dom对象
Document dom = reader.read(in);
// 获取根节点对象
Element rootElement = dom.getRootElement();
/*
* Document提供了获取根元素的方法:Element getRootElement()
*
* 而element的每一个实例用于表示当前xml文件的一个元素(一对标签),它提供了获取
* 其元素相关的方法:
* 获取当前标签的名字:String getName()
* 获取当前标签中间的文本:String getText()
* 获取当前标签下指定名字的子标签:Element element(String name)
* 获取当前标签下的所有子标签:List elements()
* 获取当前标签下指定名字的子标签:List elements(String name)
*
* 获取当前标签下指定名字标签的属性:Attribute attribute(String name)
* Attribute的每一个实例表示一个属性,它有两个方法:
* 获取属性名:String getName()
* 获取属性值:String getValue()
*/
// 获取根节点的标签名字
System.out.println("根标签:" + rootElement.getName());
// 获取指定名字的标签
List<Element> student = rootElement.elements("bean");
for (Element element : student) {
// 获取标签名字
System.out.println(element.getName());
// 获取标签属性
Attribute id = element.attribute("id");
// 获取属性值
System.out.println(id.getName() + ": " + id.getValue());
}
System.out.println("****************************************************************");
// 定义容器存放Student对象
List<Student> list = new ArrayList<>();
// 获取跟标签下的所有子标签
List<Element> elements = rootElement.elements();
for (Element element : elements) {
// 实例化student对象
Student stu = new Student();
// 获取所有子标签的名字
System.out.println("子标签名字:" + element.getName());
// 获取子标签下的所有标签元素
List<Element> elements1 = element.elements();
for (Element element1 : elements1) {
String tagName = element1.getName();
// 获取所有子孙标签的名字
System.out.println("\t\t子孙标签名字:" + tagName);
// 获取标签文本内容
String text = element1.getTextTrim();
System.out.println("\t\t\t" + element1.getName() + "的文本内容为:" + text);
}
}
}
} |
JAVA设计模式
设计模式的分类
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
还有两类:并发型模式和线程池模式。
设计模式的六大原则
1、开闭原则(Open Close Principle)
2、里氏代换原则(Liskov Substitution Principle)
3、依赖倒转原则(Dependence Inversion Principle)
4、接口隔离原则(Interface Segregation Principle)
5、迪米特法则(最少知道原则)(Demeter Principle)
6、合成复用原则(Composite Reuse Principle)
工厂方法模式(Factory Method)
工厂方法模式分为三种:
普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。首先看下关系图:
import com.design_mode.factory.Sender;
public class SenderFactory01 {
// 生成sender对象方法
public Sender product(String name){
// 判断需要生成的对象
if ("email".equals(name)) {
return new EmailSender();
} else if ("sms".equals(name)) {
return new SmsSender();
} else if ("weixin".equals(name)) {
return new WeChatSender();
} else if ("qq".equals(name)) {
return new QQSender();
} else {
throw new RuntimeException("无法生成" + name + "对象");
}
}
}
按照同样方法写出其他几个客户
import com.design_mode.factory.Sender;
public class QQSender implements Sender {
@Override
public void send() {
System.out.println("发送qq消息");
}
}
多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。关系图:
import com.design_mode.factory.Sender;
public class SenderFactory02 {
// 生成邮箱对象工厂方法
public Sender productEmail(){
return new EmailSender();
}
// 生成短信对象工厂方法
public Sender productSms(){
return new SmsSender();
}
// 生成微信对象工厂方法
public Sender productWeChat(){
return new WeChatSender();
}
// 生成QQ对象工厂方法
public Sender productQQ(){
return new QQSender();
}
}
静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。
import com.design_mode.factory.Sender;
public class SenderFactory03 {
// 生成邮箱对象工厂方法
public static Sender productEmail(){
return new EmailSender();
}
// 生成短信对象工厂方法
public static Sender productSms(){
return new SmsSender();
}
// 生成微信对象工厂方法
public static Sender productWeChat(){
return new WeChatSender();
}
// 生成QQ对象工厂方法
public static Sender productQQ(){
return new QQSender();
}
}