XML两种解析思想:DOM和SAX

一、什么是XML

英文全称为Extensible Markup Language,翻译过来为可扩展标记语言。现实生活中存在着大量的数据,在这些数据之间往往存在一定的关系,我们希望能在计算机中保存和处理这些数据的同时能够保存和处理他们之间的关系。XML就是为了解决这样的需求而产生的数据存储格式。

在XML语言中,它允许用户自定义标签。每一个标签用于描述一段数据。一个标签可以分为开始标签和结束标签,在开始标签和结束标签之间又可以嵌套其它标签,利用标签间的嵌套关系来保存数据之间的上下级关系;由于XML实质上是一段字符串,计算机可以十分方便的对他进行操作,开发人员也可以方便的阅读,因此可以说这是一种对人、对计算机都友好的数据存储格式,所以XML迅速普及,成为了一种非常常见的数据存储格式,在许多应用场景中得到应用。

二、xml的应用场景

1、传输数据

XML本质上是一段字符串,具有跨平台性的特性,因此XML常被用来在不同系统之间进行数据交换。一个典型的android应用是由服务器发送信息给android客户端后,由android客户端负责展示。此时,android客户端是java+android开发环境的。而服务器端很可能是C#+windows开发环境。如何在不同的语言、不同操作系统之间传输数据呢?XML就是一个很好的选择。

2、配置文件

XML可以在保存数据的同时保存数据之间的关系。利用这一特点,它还经常用作应用程序配置文件来使用。

三、XML检验

浏览器除了内置HTML解析器外还内置了XML解析器,因此我们可以使用浏览器对XML进行校验。

四、HTML和XML的区别

(一)语法要求不同

  1. 在HTML中不区分大小写,在XML中严格区分。
  2. 在HTML中,有时不严格,如果上下文清楚地显示出段落或者列表键在何处结尾,那么你可以省略或者
  3. 之类的结束标记。在XML中,是严格的树状结构,绝对不能省略掉结束标记。
  4. 在XML中,拥有单个标记而没有匹配的结束标记的元素必须用一个/ 字符作为结尾。这样分析器就知道不用查找结束标记了。
  5. 在XML中,属性值必须分装在引号中。在HTML中,引号是可用可不用的。
  6. 在HTML中,可以拥有不带值的属性名。在XML中,所有的属性都必须带有相应的值。
  7. 在XML文档中,空白部分不会被解析器自动删除;但是HTML是过滤掉空格的。

(二)标记不同

1、HTML使用固有的标记;而XML没有固有的标记。
2、HTML标签是预定义的;XML标签是免费的、自定义的、可扩展的。

(三)作用不同

  1. HTML是用来显示数据的;XML是用来描述数据、存放数据的,所以可以作为持久化的介质!HTML将数据和显示结合在一起,在页面中把这数据显示出来;XML则将数据和显示分开。 XML被设计用来描述数据,其焦点是数据的内容。HTML被设计用来显示数据,其焦点是数据的外观。
  2. XML 不是HTML的替代品,XML 和HTML是两种不同用途的语言。 XML 不是要替换 HTML;实际上XML 可以视作对 HTML 的补充。XML 和HTML 的目标不同HTML 的设计目标是显示数据并集中于数据外观,而XML的设计目标是描述数据并集中于数据的内容。
  3. 没有任何行为的XML。与HTML 相似,XML 不进行任何操作。(共同点)
  4. 对于XML最好的形容可能是: XML是一种跨平台的,与软、硬件无关的,处理与传输信息的工具。
  5. XML未来将会无所不在。XML将成为最普遍的数据处理和数据传输的工具。

五、XML的语法

xml的语法:文档声明、元素、属性、注释、CDATA区(转义字符)、处理指令

(一)文档声明

用来声明XML 的基本属性,用来指挥解析引擎如何去解析当前XML 。通常一个XML 都要包含并且只能包含一个文档声明。XML 的文档声明必须在整个XML 的最前面,在文档声明之前不能有任何内容。

  
version是必须存在的属性,表明当前xml所遵循规范的版本,目前位置都写1.0就可以了。
 
一定要保证xml格式的数据在保存时使用的编码和解析时使用的编码必须一致,才不会有乱码问题
 
 standalone属性用来指明当前xml是否是一个独立的xml,默认值是yes表明当前文档不需要依赖于其他文档,
如果当前文档依赖其他文档而存在则需要将此值设置为no

(二)元素

元素的语法

  1. 一个XML 标签就是一个元素
  2. 一个标签分为开始标签和结束标签
  3. 在开始标签和结束标签之间可以包含文本内容,这样的文本内容叫做标签体
  4. 如果标签的开始标签和结束标签之间不包含标签和子标签则可以将开始标签和结束标签进行合并,这样的标签就叫做自闭标签
  5. 一个标签中也可以包含任意多个子标签,但是一定要注意标签一定要合理嵌套
  6. 一个格式良好的XML 要包含并且只能包含一个根标签,其他的标签都应该是这个标签的子孙标签

元素的命名

  1. 区分大小写,例如,是两个不同的标记。
  2. 不能以数字或标点符号或”_”开头。
  3. 不能以xml(或XML、或Xml 等)开头。
  4. 不能包含空格
  5. 名称中间不能包含冒号(:)。

(三)属性

  1. 一个标签可以有多个属性,每个属性都有它自己的名称和取值,例如:
  2. 属性的名在定义时要遵循和XML 元素相同的命名规则
  3. 属性的值需要用单引号或双引号括起来

(四)注释

  1. 注释可以出现在XML 文档的任意位置除了整个文档的最前面,不能出现在文档声明之前。
  2. 注释不能嵌套注释。

(五)CDATA区/转义字符

当XML中一段内容不希望被解析器解析时可以使用CDATA区将其包住。当解析器遇到CDATA区时会将其内容当作文本对待,不会进行解析
语法:

转义字符:

&  --> &
<  --> <
>  --> >
"  --> "
'  --> '

( 六)处理指令
处理指令,简称PI (processing instruction)。处理指令用来指挥解析引擎如何解析XML文档内容。


六、XML的两种解析思想

XML编程: 利用java程序去增删改查(CRUD) XML 中的数据。

解析思想: DOM解析和SAX解析

1、DOM解析

DOM(Document Object Model) 它是 W3C 组织推荐的处理 XML 的一种方式。它会将整个XML使用类似的结构保存在内存中,再对其进行操作,所以它需要等到XML完全加载进内存才可以进行操作。它的缺点是耗费内存,当解析超大的XML时慎用。优点是可以方便的对XML 进行增删改查的操作。

在java SE的api提供了org.w3c.dom包里面有Node接口,此接口中提供了很多增删改查节点的方法,而所有文档树的对象都实现这个接口。

2、SAX解析

这种解析方式会逐行地去扫描XML文档,当遇到标签时会触发解析处理器,采用事件处理的方式解析XML (Simple API for XML) ,不是官方标准,但它是 XML 社区事实上的标准,几乎所有的 XML 解析器都支持它。优点是:在读取文档的同时即可对XML进行处理,不必等到文档加载结束,相对快捷。不需要加载进内存,因此不存在占用内存的问题,可以解析超大XML。缺点是:只能用来读取XML中数据,无法进行增删改。

它需要解析器和事件处理器,其中事件处理器里面的操作需要开发人员实现,注册到解析器中,解析器逐行扫描XML文件会调用事件处理器中的方法。

基于这两种解析思想市面上就有了很多的解析api

  1. sun jaxp既有dom方式也有sax方式,并且这套解析api已经加入到j2se的规范中,意味这不需要导入任何第三方开发包就可以直接使用这种解析方式。但是这种解析方式效率低下,没什么人用。
  2. dom4j 可以使用dom方式高效的解析xml。导入开发包,通常只需要导入核心包就可以了,如果在使用的过程中提示少什么包到lib目录下在导入缺少的包即可。

3、SAX解析使用
XML两种解析思想:DOM和SAX_第1张图片

SAX解析的步骤

//1、使用SAXParserFactory创建SAX解析工厂
    SAXParserFactory spf = SAXParserFactory.newInstance();
//2、通过SAX解析工厂得到解析器对象        
    SAXParser sp = spf.newSAXParser();
//3、通过解析器对象得到一个XML的读取器
    XMLReader xmlReader = sp.getXMLReader();
//4、设置读取器的事件处理器
    xmlReader.setContentHandler(new MyContentHandler());
//5、解析xml文件 
    xmlReader.parse("book.xml");

已知xml文件book.xml如下:



<书架> 
  <> 
    <书名>Java编程书名>  
    <作者>张三作者>  
    <售价>20.00元售价> 
  >  
  <> 
    <书名>Java设计模式书名>  
    <作者>李四作者>  
    <售价>30.00元售价> 
  > 
书架>

SAX实现输出第二本书的书名的功能源码:

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;


public class SaxDemo{
    public static void main(String[] args) throws Exception {
        //1.获取解析器工厂
        SAXParserFactory factory = SAXParserFactory.newInstance();
        //2.通过工厂获取sax解析器
        SAXParser parser = factory.newSAXParser();
        //3.获取读取器
        XMLReader reader = parser.getXMLReader();
        //4.注册事件处理器
        reader.setContentHandler(new MyContentHandler2() );
        //5.解析xml
        reader.parse("book.xml");               
    }
}

//适配器设计模式
class MyContentHandler2 extends DefaultHandler{
    private String eleName = null;
    private int count = 0;

    public void startElement(String uri, String localName, String name,
            Attributes attributes) throws SAXException {
        this.eleName = name;
    }


    public void characters(char[] ch, int start, int length)
            throws SAXException {
        if("书名".equals(eleName) && ++count==2){
            System.out.println(new String(ch,start,length));
        }
    }

    public void endElement(String uri, String localName, String name)
            throws SAXException {
        eleName = null;
    }   
}

4、DOM4J解析XML文档

Dom4j是一个简单、灵活的开放源代码的库。Dom4j是由早期开发JDOM的人分离出来而后独立开发的。与JDOM不同的是,dom4j使用接口和抽象基类,虽然Dom4j的API相对要复杂一些,但它提供了比JDOM更好的灵活性。
Dom4j是一个非常优秀的Java XML API,具有性能优异、功能强大和极易使 用的特点。现在很多软件采用的Dom4j,例如Hibernate,包括sun公司自己的JAXM也用了Dom4j。
使用Dom4j开发,需下载dom4j相应的jar文件。

XML两种解析思想:DOM和SAX_第2张图片

(一)DOM4j解析xml文件

//创建解析器:
    SAXReader reader = new SAXReader();
//利用解析器读入xml文档:
    Document   document = reader.read(new File("input.xml"));
//获取文档的根节点:
    Element root = document.getRootElement();

(二)DOM4j节点操作

//1.取得某个节点的子节点.
    Element element =ele.element(“书名");
    List elementList =ele.elements(“书名");
    List elementList =ele.elements();
//2.获取节点的名字
    node.getName();
//3.设置节点的名字
    node.setName(String newName);
//4.取得节点的文字(标签体)
    String text=node.getText()
//5.设置节点的文字(标签体)
    node.setText("aaa");
//6.添加子节点.
    ele.add(Element e);ele.addElement("age");
//7.删除子节点节点.
    parentElm.remove(childElm);
//8.获取节点类型
    node.getNodeType() ;
//9.获取父节点
    node.getParent();
//10.取得某节点对象的某属性
    Attribute attr= ele.attribute("aaa");
    Attribute attr= ele.attribute(0);
    List list = ele.attributes();
    String value = ele.attributeValue("aaa");
    Iterator it = ele.attributeIterator();
//11.设置某节点的属性
    ele.add(Attribute attr);
    ele.addAttribute(name, value);
    ele.setAttributes(List attrs);
//12.删除某属性 
    ele.remove(attribute);
/*13.在指定位置插入节点
1)得到插入位置的节点列表(list)
2)调用list.add(index,elemnent),由index决定element的插入位置。Element元素可以通过DocumentHelper对象得到。示例代码:
*/

Element aaa = DocumentHelper.createElement("aaa");
aaa.setText("aaa");

List list = root.element("书").elements();
list.add(1, aaa);

(三)DOM4j属性

//1.取得属性的名、值
    String name = attribute.getName();
    String value = attribute.getValue();
//2.设置某属性的名、值
    attribute.setName();
    attribute.setValue();

(四)DOM4j字符串和XML的转换

//1.将字符串转化为XML 
     String text = " sitinspring";Document document = DocumentHelper.parseText(text);
//2.将文档或节点的XML转化为字符串.
    String xmlStr = node.asXML();

(五)DOM4j将文档写入XML文件
方式一:
调用Node提供的write(Writer writer) 方法,使用默认方式将节点输出到流中:

    node.write(new FileWriter("book.xml"));

乱码问题:Dom4j在将文档载入内存时使用的是文档声明中encoding属性声明的编码集进行编码,如果在此时使用的writer的内部编码集与最初载入内存时使用的编码集不同则会出现乱码问题。FileWriter默认使用操作系统本地码表即gb2312编码,并且无法更改。此时可以使用如下的方式自己封装一个指定码表的Writer使用,从而解决乱码问题。

OutputStreamWriter(FileOutputStream("filePath"),"utf-8");

方式二:
利用XMLWriter写出Node:

XMLWriter writer = new XMLWriter(new  FileWriter("output.xml"));  
writer.write(node);  writer.close();

注意:使用这种方式输出时,XMLWriter首先会将内存中的docuemnt翻译成UTF-8格式的document再进行输出,这时有可能出现乱码问题。可以使用OutputFormat 指定XMLWriter转换的编码为其他编码。

OutputFormat format = OutputFormat.createPrettyPrint();             
format.setEncoding("GBK");       
XMLWriter writer = new XMLWriter(newFileWriter("output.xml"),format);

Writer使用的编码集与文档载入内存时使用的编码集不同导致乱码,使用字节流或自己封装指定编码的字符流即可。

(六)DOM4j–DocumentHelper

createDocument();
createDocument(Element rootEle
createAttribute(Element owner, String name, String value));
createElement(String name);
Docuemnt parseText(String text);

5. DOM4J编码示例
入门示例:针对上面的book.xml打印第一本书的名字:

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

public class Dom4jDemo1 {
    public static void main(String[] args) throws Exception {
        //1.获取解析器
        SAXReader reader = new SAXReader();
        //2.解析xml获取代表整个文档的dom对象
        Document dom = reader.read("book.xml");
        //3.获取根节点
        Element root = dom.getRootElement();
        //4.获取书名进行打印
        String bookName = root.element("书").element("书名").getText();
        System.out.println(bookName);
    }
}

示例2:对dom元素和属性进行增删改查的操作

import java.io.FileOutputStream;
import java.util.List;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.junit.Test;

public class Demo4jDemo2 {
    @Test
    public void attr() throws Exception{
        SAXReader reader = new SAXReader();
        Document dom = reader.read("book.xml");
        Element root = dom.getRootElement();

        Element bookEle = root.element("书");
        //bookEle.addAttribute("出版社", "传智出版社");
//      String str = bookEle.attributeValue("出版社");
//      System.out.println(str);
        Attribute attr = bookEle.attribute("出版社");
        attr.getParent().remove(attr);

        XMLWriter writer = new XMLWriter(new FileOutputStream("book.xml"),OutputFormat.createPrettyPrint());
        writer.write(dom);
        writer.close();
    }

    @Test
    public void del() throws Exception{
        SAXReader reader = new SAXReader();
        Document dom = reader.read("book.xml");
        Element root = dom.getRootElement();

        Element price2Ele = root.element("书").element("特价");
        price2Ele.getParent().remove(price2Ele);

        XMLWriter writer = new XMLWriter(new FileOutputStream("book.xml"),OutputFormat.createPrettyPrint());
        writer.write(dom);
        writer.close();
    }

    @Test
    public void update()throws Exception{
        SAXReader reader = new SAXReader();
        Document dom = reader.read("book.xml");
        Element root = dom.getRootElement();

        root.element("书").element("特价").setText("4.0元");

        XMLWriter writer = new XMLWriter(new FileOutputStream("book.xml"),OutputFormat.createPrettyPrint());
        writer.write(dom);
        writer.close();
    }

    @Test
    public void add()throws Exception{
        SAXReader reader = new SAXReader();
        Document dom = reader.read("book.xml");
        Element root = dom.getRootElement();
        //凭空创建<特价>节点,设置标签体
        Element price2Ele = DocumentHelper.createElement("特价");
        price2Ele.setText("40.0元");
        //获取父标签<书>将特价节点挂载上去
        Element bookEle = root.element("书");
        bookEle.add(price2Ele);

        //将内存中的dom树会写到xml文件中,从而使xml中的数据进行更新
//      FileWriter writer = new FileWriter("book.xml");
//      dom.write(writer);
//      writer.flush();
//      writer.close();
        XMLWriter writer = new XMLWriter(new FileOutputStream("book.xml"),OutputFormat.createPrettyPrint());
        writer.write(dom);
        writer.close();
    }

    @Test
    public void find() throws Exception{
        SAXReader reader = new SAXReader();
        Document dom = reader.read("book.xml");
        Element root = dom.getRootElement();

        List list =  root.elements();
        Element book2Ele = list.get(1);
        System.out.println(book2Ele.element("书名").getText());

    }
}

你可能感兴趣的:(java-web)