Java解析XML

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();

    }

}

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值