第二十一章 JAVA眼中的XML--文件读取

第一节 初次邂逅XML

众所周知,文件的种类是丰富多彩的,<>XML、Python、HTML、SQL、CSS、MySQL、ASP、JS、RUBY、ASP、JQ、PHP。xml作为众多文件种类的一种,经常被用作数据存储以及传输,所以xml在现今的应用程序中是非常流行的,那么在Java程序中如何识别xml文件或根据业务需求去生成一个新的xml文件呢?这就是本次学习的目的。接下来我们会初次邂逅xml,学习如何在Java程序中获取xml文件的内容,最后学习在Java文件中如何生成xml文件。xml文件的表现就是以.xml文件扩展名结尾。首先我们创建一个xml的文件,books.xml,仅仅创建是不够的,我们还要对其中的内容进行编写。那么该如何编写呢?首先关于xml的内容,它的存储结构是树形结构,说到树,我们知道树根树枝。xml文件中的内容我们就可以把它理解为一颗倒着长得树,也就是根节点长在最上面,然后依次会长出不同的子节点,如图所示
xml结构
那不同的子节点中会生出更为细小的子节点,这就是我们xml的存储结构,接下来我们来编辑books.xml文件,我们右键打开它,这里打开方式要选择无格式的文本编辑器,比如最常用的记事本去编辑xml文件,然后在这个记事本中我们需要去写这个根节点。说到根节点,每一个节点其实都是以这个<></>去包含的,也就是说每一个节点都要有一个开始标签和结束标签,然后在括号中去写我们的标签名字,也就是节点名称。书一般存放在书店中,那我们就取名字为bookstore,<bookstore></bookstore>,开始标签和结束标签名称要一致,另外注意这个节点名称是区分大小写的。这样xml文件的根节点就创建成功了,为了便于大家观察,我们可以进行节点的合理缩进,那么根节点下我们可以去创建子节点。书店下应该是一本本的书,所以我创建<book></book>,书店中可能有不同的书,所以先创建两个。书的属性,可以这么写<book id=”1”></book>,属性通常写在节点名称标签的后方,空格,属性名=””,双引号中添加属性值。第二本书我们也可以给它加ID也可以不加。这里我们是否可以把id属性作为book的子节点去存储呢,<id>2</id>其实这样也是可以的。那在实际生活需求中,我们要根据我们的逻辑去判断,到底是把这个id放在我们的属性中好还是放在我们的子节点中好,在这个book节点中,它又可以包含一些子节点,如name,作者author,year出版年限,最后再加一个常用的属性price定价,现在我们就通过book这个四个子节点来描述出了这本书所具有的一个基本的属性。第二本书包含的子节点可以与第一本书一致,当然也可以在它的基础上进行一下扩展,或者是少于这四个属性都是可以的。比如第二本书作者不详,可以不写author属性。值得一提的是,在写xml正文内容之前,要给它加一个声明,声明的格式是这样的,以<??>问号开始,然后<?xml version=”1.0”encoding=”UTF-8”?>写入xml空格加版本,现如今的xml版本都是1.0的,然后空格设置编码,这个编码我可以设置为常用的字符集,比如国际通用的utf-8。这样我们就创建完了,之后我们记得不要忘了保存。

<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
    <book id="1">
        <name>冰与火之歌</name>
        <author>乔治马丁</author>
        <year>2014</year>
        <price>89</price>
    </book>
    <book>
        <id>2</id>
        <name>安徒生童话</name>
        <year>2004</year>
        <price>77</price>
        <lauguage>English</lauguage>
    </book>
</bookstore>

在学习完xml的创建以及内容编写以后,我们要知道xml的用途通常是用来存储以及传输信息的,这儿有的人可能会有疑问,我们为什么要使用xml来存储信息呢?接下来我们来做三道思考题:

  • 不同应用程序之间的通信?举个例子我有一个订票软件和支付软件,我们需要把这两个软件配合使用才能完成我们日常订票的一个流程,那么不同的软件实现的方式肯定是不一样的,那不同的软件我们应该通过什么来传输信息呢?
  • 不同平台间的通信?举例我们现在有两个操作系统,一个是mac os系统,一个是windows系统,不同系统底层的实现机制也是不一样的,那么不同的操作系统应该通过什么去通信呢?
  • 不同平台间的数据共享?例如我们现在各大门户网站都会出一个相应的手机app,那么必然涉及到一个数据共享的问题,那么不同平台间数据共享我们要通过什么去完成呢?

答案是我们这节课学习的xml文件,也就是我们可以通过相同的xml文件,去把不同的东西联系起来,这就是我们xml的一个主要作用。说到xml文件的这个广泛应用,大家可能会说了,那你现实生活中有没有什么一个例子来说明这个xml的应用呢?这肯定是有的,如果我们使用过msn的话,那在msn中设置保存用户的聊天记录,在查看具体的内容时就是xml文件的格式。

第二节 应用DOM方式解析XML文件

下面我们学习如何在Java程序中获取xml文件的内容,在Java文件中读取xml文件的过程也称为—-解析xml文件,解析的目的:获取节点名、节点值、属性名、属性值。通常解析的方式有四种:DOM解析,SAX解析,DOM4J解析,JDOM解析。其中DOM解析,SAX解析是JAVA官方提供给我们的解析xml文档的方式,但是DOM4J解析、JDOM解析是其他的组织利用他们的方式去编写的解析xml文件的,这就使得我们在使用后两种方法时需要额外的在网上下载jar包,并导入到我们项目中去。
我们现在来做一个小例子,目的:解析我们刚刚写好的这个books.xml文件。目标:解析xml文件后,Java程序能够得到xml文件的所有数据。那么在做这个例子的时候,我们思考一下如何在Java中保留xml数据的结构,那什么是保留xml数据的结构呢?例如我们的book是bookstore下的节点,name等等是book下面的子节点,也就是说节点和节点是有一个层级关系的,大家思考如何对层级关系进行一个保留?下面是DOM解析运行的效果图
DOM解析运行效果图
遍历包括属性和节点,一本书有四个节点,并且我们还可以获取节点中文本文档的值,下面我们开始真正做一下解析xml文件的小例子。
DOM方式来解析xml文件的步骤:
1.准备工作:

  • 首先我们要创建一个DocumentBuilderFactory的对象
  • 之后在对象基础上创建一个DocumentBuilder对象
  • 通过DocumentBuilder对象的parse(String fileName)文件,我们就可以解析xml文件了返回值是一个Document文件类型的,注意选择的是 org.w3c.dom下的Document,这时准备工作就做完了。

2.解析工作:
这里我们首先面对的是获取节点以及属性的工作。实际解析时,我们是用加载了xml文件的Document对象去解析的,通过此来获取所有book节点的集合,getElementsByTagName(“book”)方法,返回值是一个NodeList方法,然后遍历该集合。注意int i=0;i<集合.getLength;这个是长度的获取方法。之后在循环体里,使用集合.item方法来获取索引位置的book节点,注意是从0开始的。此时的我们获取到了一个具体的book节点,这个不是我们的目的,我的目的其实是要遍历它的属性。大家思考一下现在我们是不知道book节点有多少个属性,以及我们也不知道属性名是什么,我们该如何去遍历它下面的属性呢?这里我们先用Node类型的引用来承装刚才获得的节点,然后调用节点.getAttributes()获取属性的方法,返回值是一个属性的Map集合,然后遍历它。获取了属性并不是我们的目的,目的是要获取它的属性名和属性值。说到属性,属性也是节点类型中的一种。说到节点类型我们再往下看,常用的节点类型如图所示
常用的节点类型
这个attr就是代表我们的属性节点,也就是代表我们刚才用get方法获取的map集合中的某一个节点,此时我们就可以想到直接使用.getNodeName就是它的名称,value就是它的返回值。如果说我们知道只有一个属性值时,还有另外的写法,将book类型直接强转为element类型,然后通过该类型获取属性值的方法来得到结果。
接下来我们学习解析book节点的子节点,此时就要用到book.getChildNodes方法来获取子节点,之后遍历输出每一个节点名和节点值。这里为什么我们输出的结果每个里面是有9个子节点呢?这是因为包括了空格和换行,这都属于上图中的Text节点类型,打印输出的节点名字都会是#text,在这里我们发现#text对我们来说并不是特别有用的,所以应该在程序中将两者区分出来,此时就用到了,.getNodeType==Node.ELMENT_NODE,如果等于这个类型,那么就输出它的名字和值就好了,此时就把文本类型的值给筛选掉了,留下的都是element型的结果。此时该如何返回element的节点值呢?结合程序运行结果以及上图我们看到,element类型的节点,getNodeValue返回的结果值都是null,这是由于我们的element类型只看name,认为节点名就是这个elementNode,而里面的书名冰与火之歌这个值实际上它认为是这个name节点的子节点,所以我们在这里要获取里面的内容,我们要先获取当前节点的子节点。总的流程也就是先获取book节点的子节点,然后再获取子节点的节点进行输出。所以这里我们先获取第一个子节点,.getFirstChild()然后获取其结果值。此时运行的结果已经基本获得了我们想要的值。在这里需要提到的是获取节点值还有另一种方法,.getTextContent()也可以直接获取到节点值,而不需要去访问它的子节点然后再得到值。那么这两种节点值获取方式有什么区别呢?我们打开这个xml文件,如果book的子节点下面依然包含其他的子节点,两种方法就会产生不同的结果,第一种方法因为得到的是element类型的节点,最终返回一个null值,第二种方法会将当前节点子节点的值以及当前节点的内容都输出。举例book下的子节点name,我们将它修改一下<name><a>aa</a>冰与火之歌</name>,使用1方法得到null,2方法返回的是aa冰与火之歌。
具体使用DOM解析的代码如下图

package DOMTest;

import org.w3c.dom.*;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;

/**
 * Created by Administrator on 2017/7/16.
 */
public class DOMTest {
    public static void main(String[] args) {
        //这里我们就可以写解析xml文件的代码books.xml文件的位置C:\Users\Administrator\Desktop
        //创建一个DocumentBuilderFactory的对象
        DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance();
        //创建一个DocumentBuilder的对象,通过上一个对象,dbf.newDocumentBuilder()方法来创建新的对象
        try {
            DocumentBuilder db=dbf.newDocumentBuilder();
            //通过DocumentBuilder对象的parse(String fileName)文件,我们就可以解析xml文件了
            Document document=db.parse("C:\\Users\\Administrator\\Desktop\\books.xml");
            //返回值是一个Document类型的,注意选择的是 org.w3c.dom下的
            //实际的解析工作是通过加载了xml文件的document类型的对象来解析的,获取所有book节点的集合
            NodeList nl=document.getElementsByTagName("book");
            //遍历该集合
            System.out.println("一共有"+nl.getLength()+"本书");
            for(int i=0;i<nl.getLength();i++){
                System.out.println("==============下面开始遍历第"+(i+1)+"本书的内容============");
                //使用item方法来获取索引位置的book节点,注意是从0开始的
                Node book=nl.item(i);//这里注意Node也是org.w3c.dom下的
                NamedNodeMap nnm=book.getAttributes();//获取book节点所有属性集合的方法,获取了所有属性
                System.out.println("第"+(i+1)+"本书中有"+nnm.getLength()+"个属性");
                for(int j=0;j<nnm.getLength();j++){
                    //通过item方法获取属性,获取属性名和属性值
                    System.out.println("这本书的第"+(j+1)+"个属性名称是"+nnm.item(j).getNodeName()
                    +",属性值是"+nnm.item(j).getNodeValue());
                }
//                Element book=(Element) (nl.item(i));//将获取的节点强制转换为Element类型
//                //通过.getAttribute方法直接获取属性值,这么写的前提是我们知道有且只有一个id属性
//                System.out.println("这本书的id属性值为"+book.getAttribute("id"));
                //获取子节点,可以通过 getChildNodes方法来获取
                NodeList childNodes=book.getChildNodes();
                //遍历输出节点名和节点值
                System.out.println("第"+(i+1)+"本书中有"+childNodes.getLength()+"个子节点");
                //打开文件可以看到是4个子节点,为什么结果是9个呢,其实很简单。包括了空格和换行
                for(int k=0;k<childNodes.getLength();k++){
                    //区分 text类型和element类型
                    if((childNodes.item(k).getNodeType()==Node.ELEMENT_NODE)){
                        //获取了element类型的节点名
                        System.out.print("第"+(k+1)+"个节点名是"+childNodes.item(k).getNodeName());
                        //获取当前节点的子节点里的内容就是我们实际的值
//                        System.out.println(",节点值是"+childNodes.item(k).getFirstChild().getNodeValue());
                        System.out.println(",节点值是"+childNodes.item(k).getTextContent());
                }
                }
                System.out.println("==============结束遍历第"+(i+1)+"本书的内容==============");
            }
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        }catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

第三节 使用SAX方式解析XML

SAX解析与我们的DOM解析原理是不一样的,DOM解析会整个将我们的xml文件都加载到我们的内存中,然后去逐个解析,那SAX解析,它是通过一个自己创建的Handler处理类去逐个分析遇到的每一个节点,这个节点分析是按顺序进行的,从最外层到最里层,依次去遇到这个节点然后将这个节点去解析出来。
SAX解析步骤:

  • 通过SAXParserFactory的静态方法newInstance()方法获取SAXParserFactory的实例Factory;
  • 通过SAXParserFactory实例的newSAXParser()方法返回newSAXParser的实例parser
  • 创建一个类继承DefaultHandler,重写其中的一些方法进行业务处理并创建这个类的实例handler。输入startElement选择重写该方法,这个是xml文件开始标签的遍历方法;同理如果要遍历结尾节点,则endElement方法一样需要重写。startDocument用来标识解析开始,endDocument用来标识解析结束。遇到了一个问题jdk.internal.org.xml.sax.helpers.DefaultHandler;一开始导入错了包,最后结果一直报错,删除掉前面的jdk.internal.结果就可以了。
    当解析器读取到xml的第一行的时候,也就是声明句子时,认为这个xml的文件解析就开始了,走到最后也就意味着xml文件的解析结束了,这里光有startElement和endElement是不够的,这里我们还要去具体解析其中的某一个节点甚至是节点的属性。那么我们来看一下该如何去解析节点的属性呢?如果要想在SAX里解析我们还要重写一个方法startElement。里面的属性.getValue()方法,可以传一个String类型的参数进来,String类型的参数实际上就是我们book的属性名。

当我们完成了用SAX解析xml文件之后,我们之前有提到一个问题,如何在java中保留xml的数据结构。这里主要考验一个问题,我们知道遍历book的子节点时,有一个startElement,endElement以及,characters方法,三个方法的数据怎么分享呢?这时我们知道第三个方法得到的是我们的具体文本值,此时可以把它设置成一个全局变量,然后set进去值。具体的代码如下:

import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;

/**
 * Created by Administrator on 2017/7/20.
 */
public class SAXTest {
    public static void main(String[] args) {
        //获取一个SAXParserFactory的实例
        SAXParserFactory factory=SAXParserFactory.newInstance();
        //获取一个SAXParser的实例parser
        try {
            SAXParser parser=factory.newSAXParser();
            //创建一个SAXParserHandler对象并传递进去
            SAXParserHandler handler=new SAXParserHandler();
            parser.parse("C:\\Users\\Administrator\\Desktop\\books.xml",handler);
            System.out.println("~!~!~!共有"+handler.getBookList().size()+"本书");
            for(Book book:handler.getBookList()){
                System.out.println(book.getId());
                System.out.println(book.getName());
                System.out.println(book.getAuthor());
                System.out.println(book.getYear());
                System.out.println(book.getPrice());
                System.out.println(book.getLanguage());
                System.out.println("-----finish------");
            }
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch(IOException e){
            e.printStackTrace();
        }
    }
}
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import java.util.ArrayList;

/**
 * Created by Administrator on 2017/7/20.
 */
public class SAXParserHandler extends DefaultHandler {
    int bookIndex=0;
    String value=null;
    Book book=null;
    private ArrayList<Book> bookList=new ArrayList<Book>();

    public ArrayList<Book> getBookList() {
        return bookList;
    }

    //用来遍历xml文件的开始标签
    @Override
    public void startElement(String s, String s1, String s2, Attributes attributes) throws SAXException {
        //调用父类的DefaultHandler的startElement方法
        super.startElement(s, s1, s2, attributes);
        //如果节点名称等于book,开始解析book元素的属性
        if(s2.equals("book")){
            bookIndex++;
            book=new Book();
//            //假设我们已知它的属性名称id,根据属性名称来获取属性值
//            System.out.println("book的属性值是:"+attributes.getValue("id"));
            //如果我们不知道它有几个属性呢?也不知道名称该如何获取呢?
            System.out.println("===============开始遍历第"+bookIndex+"本书的内容==================");
            for(int i=0;i<attributes.getLength();i++){
                System.out.print("第"+bookIndex+"本book的第"+(i+1)+"个属性值是:"+attributes.getQName(i));
                System.out.println(",属性值是:"+attributes.getValue(i));
                //在这里我们发现两本书的属性一起出现了,那么我们该怎么分开显示呢?这里就要用的endElement方法
                //设置属性值
                if(attributes.getQName(i).equals("id")) {
                    book.setId(attributes.getValue(i));
                }
            }
        }else if(!s2.equals("book")&&!s2.equals("bookstore")){
            System.out.print("节点名是:"+s2);
        }
    }
    //用来遍历xml文件的结束标签
    @Override
    public void endElement(String s, String s1, String s2) throws SAXException {
        //调用父类的DefaultHandler的endElement方法
        super.endElement(s, s1, s2);
        if(s2.equals("book")){
            bookList.add(book);
            book=null;
            System.out.println("===============结束遍历第"+bookIndex+"本书的内容==================");
        }else if(s2.equals("name")) {
            book.setName(value);
        }else if(s2.equals("author")) {
            book.setAuthor(value);
        }else if(s2.equals("year")) {
            book.setYear(value);
        }else if(s2.equals("price")) {
            book.setPrice(value);
        }else if(s2.equals("language")) {
            book.setLanguage(value);
        }
    }
    //用来标识解析开始
    @Override
    public void startDocument() throws SAXException {
        super.startDocument();
        System.out.println("SAX解析开始...");
    }
    //用来标识解析结束
    @Override
    public void endDocument() throws SAXException {
        super.endDocument();
        System.out.println("SAX解析结束...");
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        super.characters(ch, start, length);
        value=new String(ch,start,length);
        if(!value.trim().equals("")){
            System.out.println(",节点值是"+value);

        }
    }
}
public class Book {
    private String id;
    private String name;
    private String author;
    private String year;
    private String price;
    private String language;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    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 getYear() {
        return year;
    }

    public void setYear(String year) {
        this.year = year;
    }

    public String getPrice() {
        return price;
    }

    public void setPrice(String price) {
        this.price = price;
    }

    public String getLanguage() {
        return language;
    }

    public void setLanguage(String language) {
        this.language = language;
    }
}

第四节 JDOM及DOM4J解析XML文件

之前我们已经学习了java官方的两种解析方式,接下来我们学习不是java官方的两种解析方式JDOM和DOM4J解析,首先来看JDOM解析,导入JDOMjar包,这时我们就可以开始books.xml文件的JDOM解析工作了。下面开始准备工作:

  • 创建Saxbuilder的对象 注意这里是org.jdom2.input包下的;
  • 创建一个输入流,将xml文件加载到输入流中;
  • 通过saxBuilder的build方法将输入流加载到saxBuilder。这里注意build方法返回值是Document类型,也是org.jdom2.input包里的。
  • 那么获取到这个document有什么用呢?它的getRootElement()方法来获取我们这个xml的文件的根节点,那么为什么我们要获取这个根节点呢?因为我们要根据根节点来获取bookstore下面所有的子节点
  • 下面通过上面得到的element根节点,它的getChildren方法来获取子节点,通过代码可以看到返回的结果是一个elmentList集合子节点集合,获取集合之后我们就可以继续往下解析了。
    5个步骤的准备工作做完之后我们获取到了这个bookList,也就是这个bookList现在目前有两本书,这里我们可以采取foreach的方式来遍历。
import org.jdom2.Attribute;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;

import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * Created by Administrator on 2017/7/22.
 */
public class JDOMTest {
    private static ArrayList<Book> booksList=new ArrayList<Book>();

    public ArrayList<Book> getBooksList() {
        return booksList;
    }

    public static void main(String[] args) {
        //进行对books.xml文件的JDOM解析
        //准备工作
        //1.创建一个SAXBuilder的对象
        SAXBuilder saxBuilder =new SAXBuilder();
        try {
            //2.创建一个输入流,将xml文件加载到输入流中
            FileInputStream  in=new FileInputStream("C:\\Users\\Administrator\\Desktop\\books2.xml");
            //出现乱码时的处理方法,按utf-8的编码去读取读入的信息
            InputStreamReader isr=new InputStreamReader(in,"gbk");
            //3.通过saxBuilder的build方法将输入流加载到saxBuilder
            Document document=saxBuilder.build(isr);
            //4.通过document对象的getRootElement来获取xml文件的根节点
            Element rootElement=document.getRootElement();
            //5.获取根节点下的子节点的集合
            List<Element> bookList =rootElement.getChildren();
            //继续进行解析
            for(Element book:bookList){
                Book bookEntry=new Book();
                System.out.println("======开始解析第"+(bookList.indexOf(book)+1)+"本书======");
                //解析book的属性
                List<Attribute> attributesList=book.getAttributes();
//                //如果知道属性名id,只有一个属性时也可以这么写
//                book.getAttribute("id");//获取id的这个属性信息,还需要额外输出
//                book.getAttributeValue("id");//获取id的这个属性的具体值
                //遍历属性集合,这个方法针对的是不清楚属性名和属性数量的情况
                for(Attribute attr:attributesList){
                    //获取属性名
                    String s1=attr.getName();
                    //获取属性值
                    String s2=attr.getValue();
                    //这里需要注意和DOM解析方式相比,这个获取到的不论是什么类型,得到的都是名字以及文本,没有空格
                    System.out.println("属性名:"+s1+",属性值:"+s2);
                    if(s1.equals("id")){
                        bookEntry.setId(s2);
                    }
                }
                //接下来我们学习对book的子节点,节点名和节点值的遍历
                List<Element> bookChildList=book.getChildren();
                for(Element bookChild:bookChildList){
                    System.out.println("节点名:"+bookChild.getName()+",节点值:"+bookChild.getValue());
                    if(bookChild.getName().equals("name")) {
                        bookEntry.setName(bookChild.getValue());
                    }else if(bookChild.getName().equals("author")) {
                        bookEntry.setAuthor(bookChild.getValue());
                    }else if(bookChild.getName().equals("year")) {
                        bookEntry.setYear(bookChild.getValue());
                    }else if(bookChild.getName().equals("price")) {
                        bookEntry.setPrice(bookChild.getValue());
                    }else if(bookChild.getName().equals("language")) {
                        bookEntry.setLanguage(bookChild.getValue());
                    }
                }
                booksList.add(bookEntry);
                System.out.println("======结束解析第"+(bookList.indexOf(book)+1)+"本书======");
                bookEntry=null;
                System.out.println(bookList.size());
                for(Book book2:booksList){
                    System.out.println(book2.getId());
                    System.out.println(book2.getName());
                    System.out.println(book2.getAuthor());
                    System.out.println(book2.getYear());
                    System.out.println(book2.getPrice());
                    System.out.println(book2.getLanguage());
                    System.out.println("-----finish------");
                }
            }
            isr.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch (JDOMException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

JDOM常见的解析问题,乱码如何处理:数字一般很少出现乱码,如果中文出现乱码时,我们首先考虑xml文件,先看一下xml文件第一行声明的编码,如果是编码有问题可以修改一下编码,然后看是否会正常的解析。这个是简单的修改问题。但有时候我们也会遇到一些比较麻烦的时候,修改后仍然不能正常解析的,就需要我们对代码进行一些处理了。如何在不修改编码的情况下去正常得到结果呢?其实在输入流我们在进行转换的时候,就可以直接指定编码的形式。
DOM4J解析代码如下:

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

import java.io.File;
import java.util.List;
import java.util.Iterator;

/**
 * Created by Administrator on 2017/7/23.
 */
public class DOM4JText {
    public static void main(String[] args) {
        //解析xml文件
        //创建SAXReader的对象reader,DOM4J包下的
        SAXReader reader=new SAXReader();
        try {
            //通过reader对象的reader方法加载books.xml文件,注意返回值是dom4j文件下的,Document类型
            Document document=reader.read(new File("C:\\Users\\Administrator\\Desktop\\books2.xml"));
            //获取了document对象之后可以获取它的根节点,获取根节点是为了遍历其子节点的信息
            Element bookStore=document.getRootElement();
            //通过element对象的elementIterator()方法获取一个元素的迭代器
            Iterator it=bookStore.elementIterator();
            //遍历迭代器,获取根节点中的信息(书籍)。
            while(it.hasNext()){
                //获取book
                System.out.println("==============开始遍历某一本书==============");
                Element book=(Element) it.next();
                //获取book的属性名和属性值
                List<Attribute> bookA=book.attributes();
                for(Attribute a:bookA){
                    System.out.println("属性名:"+a.getName()+",属性值:"+a.getValue());
                }
                //接下来该如何遍历子节点呢?这时我们知道根节点element有一个迭代器可以获取一个迭代器去遍历book
                //我们也可以获取遍历book子节点的迭代器去遍历book的子节点
                Iterator it2=book.elementIterator();
                while(it2.hasNext()){
                    Element it3=(Element)it2.next();
                    System.out.println("节点名:"+it3.getName()+",节点值:"+it3.getStringValue());
                }
                System.out.println("==============结束遍历某一本书==============");
            }

        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }
}

第五节 四种XML解析方式大PK

四种解析方式的对比,这里我们会从整体上对比,然后会从意义和性能上进行对比,我们来看基础的解析方式,有两种DOM SAX,什么是基础的方法呢?就是不需要导入jar包,java官方提供给我们的解析方式。值得一提的是DOM是与平台无关的官方解析方式,也就是不止在java平台下有这个DOM解析,其他很多平台下都有DOM解析。SAX就是JAVA提供给我们的基于事件驱动的解析方式。然后我们看JAVA在这两种基础方法之上扩展的两种解析方法,一种是JDOM解析,一种是DOM4J解析,JDOM和DOM4J都是在基础的方法上扩展的,只有java中能够使用的解析方法。也就是别的平台没有这个JDOM和DOM4J解析。接下来我们看DOM解析的一个运行过程,我们通过这个图解来直观的理解一下运行的过程。DOM在解析时会一次性的将xml文件加载到内存中去,形成DOM树。这个时候问题就来了一次性加载,如果xml特别大的时候,大家思考这个时候会不会比较浪费时间呢?这个对内存的性能要求也是很高的。DOM解析图解
接下来我们来看SAX解析,这个SAX解析它是基于事件的一种解析,什么叫基于事件呢?也就是我们的xml文件放在这,它解析的过程是一步一步解析的,逐条语句去看,每走一条语句然后去判断触发我们handler中哪一个事件方法。比如说它走到我们第一句声明的时候,就会触发startDocument方法,接下来走到bookstore以及book很多开始标签,这时就会触发startElement方法,结尾标签就是endElement方法,最后走完根节点就会触发我们的endDocument方法,这就是我们的SAX解析。SAX解析图解
同样是基本的JAVA提供给我们的解析XML的方法,我们解析时选择DOM还是SAX?这里我们首先看DOM。
优点:形成树结构,直观好理解,代码易编写。解析过程中树结构会保留在内存中,方便修改
缺点:当xml文件较大的,对内存耗费比较大,容易影响解析性能并造成内存溢出。

接下来我们看SAX解析。
SAX优点:采用事件驱动模式,对内存耗费较小,适用于只需要处理xml中数据时,也就是说我们对SAX的结构不去关心,哪个节点包含在哪个节点之内,只关心节点之间的值或者属性值等等节点中的数据,这时我们就优先考虑SAX解析,当然它也是有缺点的。
缺点:不易编码,这里我们在用SAX解析添加对象的时候就比DOM模式麻烦。很难同时访问同一个xml文件中的多处不同数据
我们可以根据实际情况去选择是DOM解析还是SAX解析。
接下来我们来看JDOM,DOM4J的一个比较。
JDOM:仅使用具体类而不使用接口,大量使用了集合Collections类
DOM4J 最初是JDOM的一种智能分支,它合并了很多超出基本xml文档表示的功能,DOM4J使用接口和抽象基本类方法,是一个优秀的JAVA xmlAPI,具有性能优异,灵活性好,功能强大和极端易使用的特点,是一个开放源代码的软件。四种解析速度大比拼,一般来说解析速度越快肯定也就越好,当然这不是唯一的指标,却也是重要的指标。
这里最后的代码就不写了,自己用junit4添加了一个测试方法去比较最后的速度
DOM:110
SAX:10
JDOM:95
DOM4J:56

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值