• Java文件操作①——XML文件的读取


    一、邂逅XML

    文件种类是丰富多彩的,XML作为众多文件类型的一种,经常被用于数据存储和传输。所以XML在现今应用程序中是非常流行的。本文主要讲Java解析和生成XML。用于不同平台、不同设备间的数据共享通信。

    XML文件的表现:以“.xml”为文件扩展名的文件;

       存储结构:树形结构;

    节点名称区分大小写。

    1、<book id="1"></book> id为属性, <book><id>1</id></book> id为节点
    2、xml文件开头要加上版本信息和编码方式<?xml version="1.0" encoding="UTF-8"?>

     比如:

    ❤ 为什么要使用XML?

    思考1:不同应用程序之间的通信?

    思考2:不同平台间的通信?

    思考3:不同平台间的数据共享?

    答案就是我们要学习的XML文件。我们可以使用相同的xml把不同的文件联系起来

    二、应用 DOM 方式解析 XML

    ❤ 在Java程序中如何获取XML文件的内容

    解析的目的:获取节点名、节点值、属性名、属性值;

    四种解析方式:DOM、SAX、DOM4J、JDOM 

    DOM、SAX :java 官方方式,不需要下载jar包
    DOM4J、JDOM :第三方,需要网上下载jar包

    示例:解析XML文件,目标是解析XML文件后,Java程序能够得到xml文件的所有数据

    思考:如何在Java程序中保留xml数据的结构?

    如何保留节点之间的层级关系?

    注意常用的节点类型:

     下面介绍DOM方式解析XML:

     功能说明:

    代码示例:

     1 package com.study.domtest;
     2 
     3 import java.io.IOException;
     4 
     5 import javax.xml.parsers.DocumentBuilder;
     6 import javax.xml.parsers.DocumentBuilderFactory;
     7 import javax.xml.parsers.ParserConfigurationException;
     8 
     9 import org.w3c.dom.Document;
    10 import org.w3c.dom.NamedNodeMap;
    11 import org.w3c.dom.Node;
    12 import org.w3c.dom.NodeList;
    13 import org.xml.sax.SAXException;
    14 
    15 /**
    16  * DOM方式解析xml
    17  */
    18 public class DOMTest {
    19 
    20     public static void main(String[] args) {
    21         //1、创建一个DocumentBuilderFactory的对象
    22         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    23         //2、创建一个DocumentBuilder的对象
    24         try {
    25             //创建DocumentBuilder对象
    26             DocumentBuilder db = dbf.newDocumentBuilder();
    27             //3、通过DocumentBuilder对象的parser方法加载books.xml文件到当前项目下
    28             /*注意导入Document对象时,要导入org.w3c.dom.Document包下的*/
    29             Document document = db.parse("books.xml");//传入文件名可以是相对路径也可以是绝对路径
    30             //获取所有book节点的集合
    31             NodeList bookList = document.getElementsByTagName("book");
    32             //通过nodelist的getLength()方法可以获取bookList的长度
    33             System.out.println("一共有" + bookList.getLength() + "本书");
    34             //遍历每一个book节点
    35             for (int i = 0; i < bookList.getLength(); i++) {
    36                 System.out.println("=================下面开始遍历第" + (i + 1) + "本书的内容=================");
    37         //❤未知节点属性的个数和属性名时:
    38                 //通过 item(i)方法 获取一个book节点,nodelist的索引值从0开始
    39                 Node book = bookList.item(i);
    40                 //获取book节点的所有属性集合
    41                 NamedNodeMap attrs = book.getAttributes();
    42                 System.out.println("第 " + (i + 1) + "本书共有" + attrs.getLength() + "个属性");
    43                 //遍历book的属性
    44                 for (int j = 0; j < attrs.getLength(); j++) {
    45                     //通过item(index)方法获取book节点的某一个属性
    46                     Node attr = attrs.item(j);
    47                     //获取属性名
    48                     System.out.print("属性名:" + attr.getNodeName());
    49                     //获取属性值
    50                     System.out.println("--属性值" + attr.getNodeValue());
    51                 }
    52           //❤已知book节点有且只有1个id属性:
    53              /* 
    54               //前提:已经知道book节点有且只能有1个id属性
    55               //将book节点进行强制类型转换,转换成Element类型
    56               Element book1 = (Element) bookList.item(i);
    57               //通过getAttribute("id")方法获取属性值
    58               String attrValue = book1.getAttribute("id");
    59               System.out.println("id属性的属性值为" + attrValue);
    60               */
    61                 
    62              //解析book节点的子节点
    63                 NodeList childNodes = book.getChildNodes();
    64               //遍历childNodes获取每个节点的节点名和节点值
    65                 System.out.println("第" + (i+1) + "本书共有" + childNodes.getLength() + "个子节点");
    66                 for (int k = 0; k < childNodes.getLength(); k++) {
    67                   //区分出text类型的node以及element类型的node
    68                     if(childNodes.item(k).getNodeType() == Node.ELEMENT_NODE){
    69                       //获取了element类型节点的节点名
    70                         System.out.print("第" + (k + 1) + "个节点的节点名:" + childNodes.item(k).getNodeName());
    71                       //获取了element类型节点的节点值
    72                         System.out.println("--节点值是:" + childNodes.item(k).getFirstChild().getNodeValue());
    73 //                        System.out.println("--节点值是:" + childNodes.item(k).getTextContent());
    74                     }
    75                 }
    76                 System.out.println("======================结束遍历第" + (i + 1) + "本书的内容=================");
    77             }
    78 
    79         } catch (ParserConfigurationException e) {
    80             e.printStackTrace();
    81         } catch (SAXException e) {
    82             e.printStackTrace();
    83         } catch (IOException e) {
    84             e.printStackTrace();
    85         }
    86     }
    87 
    88 }

     三、应用 SAX 方式解析 XML

    SAX是SIMPLE API FOR XML的缩写,与DOM比较而言,SAX是一种轻量型的方法。

    Dom解析会将整个xml文件加载到内存中,然后再逐个解析
    Sax解析是通过Handler处理类逐个依次解析每个节点

    在处理DOM的时候,我们需要读入整个的XML文档,然后在内存中创建DOM树,生成DOM树上的每个NODE对象。当文档比较小的时候,这不会造成什么问题,但是一旦文档大起来,处理DOM就会变得相当费时费力。特别是其对于内存的需求,也将是成倍的增长,以至于在某些应用中使用DOM是一件很不划算的事。这时候,一个较好的替代解决方法就是SAX。 SAX在概念上与DOM完全不同。首先,不同于DOM的文档驱动,它是事件驱动的,也就是说,它并不需要读入整个文档,而文档的读入过程也就是SAX的解析过程。所谓事件驱动,是指一种基于回调(callback)机制的程序运行方法。在XMLReader接受XML文档,在读入XML文档的过程中就进行解析,也就是说读入文档的过程和解析的过程是同时进行的,这和DOM区别很大。

    ❤ 

    代码示例:Book实体类

     1 package com.study.saxtest.entity;
     2 
     3 /**
     4  * 用Book实体类代表XML文件中的"<book>...</book>"中整个元素
     5  * 在遇到<book>标签,证明我们要存储新的book时需要创建Book对象
     6  */
     7 public class Book {
     8     private String id;
     9     private String name;
    10     private String author;
    11     private String year;
    12     private String price;
    13     private String language;
    14     public String getId() {
    15      return id;
    16     }
    17     public void setId(String id) {
    18      this.id = id;
    19     }
    20     public String getName() {
    21      return name;
    22     }
    23     public void setName(String name) {
    24      this.name = name;
    25     }
    26     public String getAuthor() {
    27      return author;
    28     }
    29     public void setAuthor(String author) {
    30      this.author = author;
    31     }
    32     public String getYear() {
    33      return year;
    34     }
    35     public void setYear(String year) {
    36      this.year = year;
    37     }
    38     public String getPrice() {
    39      return price;
    40     }
    41     public void setPrice(String price) {
    42      this.price = price;
    43     }
    44     public String getLanguage() {
    45      return language;
    46     }
    47     public void setLanguage(String language) {
    48      this.language = language;
    49     }
    50 }

    SAXParserHandler类:

      1 package com.study.saxtest.handler;
      2 
      3 import java.util.ArrayList;
      4 
      5 import org.xml.sax.Attributes;
      6 import org.xml.sax.SAXException;
      7 import org.xml.sax.helpers.DefaultHandler;
      8 
      9 import com.study.saxtest.entity.Book;
     10 
     11 public class SAXParserHandler extends DefaultHandler{
     12     /*注意DefaultHandler是org.xml.sax.helpers包下的*/
     13     
     14     int bookIndex = 0;//设置全局变量,用来记录是第几本书
     15     
     16     String value = null;
     17     Book book = null;
     18     private ArrayList<Book> bookList = new ArrayList<Book>();//保存book对象
     19 
     20     public ArrayList<Book> getBookList() {
     21         return bookList;
     22     }
     23 
     24     /**
     25      * 用来标识解析开始
     26      */
     27     @Override
     28     public void startDocument() throws SAXException {
     29         super.startDocument();
     30         System.out.println("SAX解析开始");
     31 
     32     }
     33     
     34     /**
     35      * 用来标识解析结束
     36      */
     37     @Override
     38     public void endDocument() throws SAXException {
     39         super.endDocument();
     40         System.out.println("SAX解析结束");
     41     }
     42     
     43     /**
     44      * 用来遍历xml文件的开始标签
     45      * 解析xml元素
     46      */
     47     @Override
     48     public void startElement(String uri, String localName, String qName,Attributes attributes) throws SAXException {
     49         //调用DefaultHandler类的startElement方法
     50         super.startElement(uri, localName, qName, attributes);
     51         if (qName.equals("book")) {
     52             bookIndex++;
     53             //创建一个book对象
     54             /*Book*/ book = new Book();
     55             //开始解析book元素的属性
     56             System.out.println("======================开始遍历第"+bookIndex+"本书的内容=================");
     57            /* //❤已知节点的属性名时:比如已知id属性,根据属性名称获取属性值
     58             String value = attributes.getValue("id");
     59             System.out.print("book的属性值是:"+value);*/
     60           //❤未知节点的属性名时,获取属性名和属性值
     61             int num=attributes.getLength();
     62             for(int i=0;i<num;i++){
     63                 System.out.print("book元素的第"+(i+1)+"个属性名是:"+attributes.getQName(i));
     64                 System.out.println("---属性值是:"+attributes.getValue(i));
     65                 if (attributes.getQName(i).equals("id")) {//往book对象中塞值
     66                     book.setId(attributes.getValue(i));
     67                 }
     68             }
     69         }else if (!qName.equals("book") && !qName.equals("bookstore")) {
     70             System.out.print("节点名是:" + qName + "---");//此时qName获取的是节点名(标签)
     71         }
     72     }
     73     
     74     /**
     75      * 用来遍历xml文件的结束标签
     76      */
     77     @Override
     78     public void endElement(String uri, String localName, String qName) throws SAXException {
     79         //调用DefaultHandler类的endElement方法
     80         super.endElement(uri, localName, qName);
     81         //判断是否针对一本书已经遍历结束
     82         if (qName.equals("book")) {
     83             bookList.add(book);//在清空book对象之前先保存
     84             book = null;//把book清空,方便解析下一个book节点
     85             System.out.println("======================结束遍历第"+bookIndex+"本书的内容=================");
     86         }else if (qName.equals("name")) {
     87             book.setName(value);
     88         }
     89         else if (qName.equals("author")) {
     90          book.setAuthor(value);
     91         }
     92         else if (qName.equals("year")) {
     93          book.setYear(value);
     94         }
     95         else if (qName.equals("price")) {
     96          book.setPrice(value);
     97         }
     98         else if (qName.equals("language")) {
     99          book.setLanguage(value);
    100         }
    101     }
    102     
    103     /**
    104      * 获取文本
    105      * 重写charaters()方法时,
    106      * String(byte[] bytes,int offset,int length)的构造方法进行数组的传递
    107      * 去除解析时多余空格
    108      */
    109     @Override
    110     public void characters(char[] ch, int start, int length)throws SAXException {
    111         /**
    112          * ch 代表节点中的所有内容,即每次遇到一个标签调用characters方法时,数组ch实际都是整个XML文档的内容
    113          * 如何每次去调用characters方法时我们都可以获取不同的节点属性?这时就必须结合start(开始节点)和length(长度)
    114          */
    115         super.characters(ch, start, length);
    116         /*String */value = new String(ch, start, length);//value获取的是文本(开始和结束标签之间的文本)
    117 //        System.out.println(value);//输出时会多出两个空格,是因为xml文件中空格与换行字符被看成为一个文本节点
    118         if(!value.trim().equals("")){//如果value去掉空格后不是空字符串
    119             System.out.println("节点值是:" + value);
    120         }
    121     }
    122     
    123     /**
    124      * qName获取的是节点名(标签)
    125      * value获取的是文本(开始和结束标签之间的文本)
    126      * 思考:qName和value分别在两个方法中,如何将这两个方法中的参数整合到一起?
    127      * 分析:要在两个方法中用同一个变量,就设置成全局变量,可以赋初值为null。
    128      *     可以把characters()方法中的value作成一个全局变量
    129      * 
    130      * 然后在endElement()方法中对book对象进行塞值。记得要把Book对象设置为全局变量,变量共享
    131      */
    132 }

    测试类:SAXTest

     1 package com.study.saxtest.test;
     2 
     3 import java.io.IOException;
     4 
     5 import javax.xml.parsers.ParserConfigurationException;
     6 import javax.xml.parsers.SAXParser;
     7 import javax.xml.parsers.SAXParserFactory;
     8 
     9 import org.xml.sax.SAXException;
    10 
    11 import com.study.saxtest.entity.Book;
    12 import com.study.saxtest.handler.SAXParserHandler;
    13 
    14 /**
    15  * sax方式解析XML
    16  */
    17 public class SAXTest {
    18 
    19     public static void main(String[] args) {
    20         //1.获取一个SAXParserFactory的实例对象
    21         SAXParserFactory factory = SAXParserFactory.newInstance();
    22         //2.通过factory的newSAXParser()方法获取一个SAXParser类的对象。
    23         try {
    24             SAXParser parser = factory.newSAXParser();
    25             //创建SAXParserHandler对象
    26             SAXParserHandler handler = new SAXParserHandler();
    27             parser.parse("books.xml", handler);
    28             System.out.println("~~~~~共有"+handler.getBookList().size()+"本书");
    29             for (Book book : handler.getBookList()) {
    30                 System.out.println(book.getId());
    31                 System.out.println(book.getName());
    32                 System.out.println(book.getAuthor());
    33                 System.out.println(book.getYear());
    34                 System.out.println(book.getPrice());
    35                 System.out.println(book.getLanguage());
    36                 System.out.println("----finish----");
    37             }
    38         } catch (ParserConfigurationException e) {
    39             e.printStackTrace();
    40         } catch (SAXException e) {
    41             e.printStackTrace();
    42         } catch (IOException e) {
    43             e.printStackTrace();
    44         }
    45     }
    46 
    47 }

     运行结果:

    SAX解析开始
    ======================开始遍历第1本书的内容=================
    book元素的第1个属性名是:id---属性值是:1
    节点名是:name---节点值是:冰与火之歌
    节点名是:author---节点值是:乔治马丁
    节点名是:year---节点值是:2014
    节点名是:price---节点值是:89
    ======================结束遍历第1本书的内容=================
    ======================开始遍历第2本书的内容=================
    book元素的第1个属性名是:id---属性值是:2
    节点名是:name---节点值是:安徒生童话
    节点名是:year---节点值是:2004
    节点名是:price---节点值是:77
    节点名是:language---节点值是:English
    ======================结束遍历第2本书的内容=================
    SAX解析结束
    ~~~~~共有2本书
    1
    冰与火之歌
    乔治马丁
    2014
    89
    null
    ----finish----
    2
    安徒生童话
    null
    2004
    77
    English
    ----finish----
    View Code

     四、应用 DOM4J 及 JDOM 方式解析 XML

     # JDOM 方式解析 XML

      JDOM 开始解析前的准备工作:

      JDOM是第三方提供的解析XML方法,需要jdom-2.0.5.jar包

     

     示例代码:

      1 package com.study.jdomtest1.test;
      2 
      3 import java.io.FileInputStream;
      4 import java.io.FileNotFoundException;
      5 import java.io.IOException;
      6 import java.io.InputStream;
      7 import java.io.InputStreamReader;
      8 import java.util.ArrayList;
      9 import java.util.List;
     10 
     11 import org.jdom2.Attribute;
     12 import org.jdom2.Document;
     13 import org.jdom2.Element;
     14 import org.jdom2.JDOMException;
     15 import org.jdom2.input.SAXBuilder;
     16 
     17 import com.study.jdomtest1.entity.Book;
     18 
     19 /**
     20  *  JDOM 解析XML
     21  */
     22 public class JDOMTest {
     23     private static ArrayList<Book> booksList = new ArrayList<Book>();
     24 
     25     public static void main(String[] args) {
     26         // 进行对books.xml文件的JDOM解析
     27         //❤准备工作
     28         // 1.创建一个SAXBuilder的对象
     29         SAXBuilder saxBuilder = new SAXBuilder();//注意SAXBuilder是org.jdom2.input包下的
     30         InputStream in;
     31         try {
     32             // 2.创建一个输入流,将xml文件加载到输入流中
     33             in=new FileInputStream("books.xml");//如果将xml文件放在src/res包下,此时应该输入“src/res/books.xml”
     34             InputStreamReader isr = new InputStreamReader(in, "UTF-8");//使用包装流InputStreamReader进行读取编码的指定,防止乱码
     35             // 3.通过saxBuilder的build方法,将输入流加载到saxBuilder中
     36             Document document = saxBuilder.build(isr);
     37             // 4.通过document对象获取xml文件的根节点
     38             Element rootElement = document.getRootElement();
     39             // 5.获取根节点下的子节点的List集合
     40             List<Element> bookList = rootElement.getChildren();
     41             //❤ 继续解析,采用for循环对bookList进行遍历
     42             for (Element book : bookList) {
     43                 Book bookEntity = new Book();
     44                 System.out.println("======开始解析第" + (bookList.indexOf(book) + 1) + "书======");//indexOf()返回的是index的位置,是从0开始
     45                 // 解析book的属性集合
     46                 List<Attribute> attrList = book.getAttributes();//适用于未知属性情况下
     47                 /*//知道节点下属性名称时,获取节点值
     48                 book.getAttributeValue("id");*/
     49                 // 遍历attrList(针对不清楚book节点下属性的名字及数量)
     50                 for (Attribute attr : attrList) {
     51                     /**注:JDom中,Attribute的getName和getValue方法获取到的都是实际的名称和值,
     52                      * 不会出现SAX和DOM中的空格和换行的情况*/
     53                     // 获取属性名
     54                     String attrName = attr.getName();
     55                     // 获取属性值
     56                     String attrValue = attr.getValue();
     57                     System.out.println("属性名:" + attrName + "----属性值:" + attrValue);
     58                     if (attrName.equals("id")) {
     59                         bookEntity.setId(attrValue);
     60                        }
     61                 }
     62                 
     63                 //❤对book节点的子节点的节点名以及节点值的遍历
     64                 List<Element> bookChilds = book.getChildren();
     65                 for (Element child : bookChilds) {
     66                     System.out.println("节点名:" + child.getName() + "----节点值:" + child.getValue());
     67                     if (child.getName().equals("name")) {
     68                         bookEntity.setName(child.getValue());
     69                        }
     70                        else if (child.getName().equals("author")) {
     71                         bookEntity.setAuthor(child.getValue());
     72                        }
     73                        else if (child.getName().equals("year")) {
     74                         bookEntity.setYear(child.getValue());
     75                        }
     76                        else if (child.getName().equals("price")) {
     77                         bookEntity.setPrice(child.getValue());
     78                        }
     79                        else if (child.getName().equals("language")) {
     80                         bookEntity.setLanguage(child.getValue());
     81                        }
     82 
     83                 }
     84                 System.out.println("======结束解析第" + (bookList.indexOf(book) + 1) + "书======");
     85                 booksList.add(bookEntity);
     86                 bookEntity = null;
     87                 //测试数据
     88                 System.out.println(booksList.size());
     89                 System.out.println(booksList.get(0).getId());
     90                 System.out.println(booksList.get(0).getName());
     91 
     92             }
     93         } catch (FileNotFoundException e) {
     94             e.printStackTrace();
     95         } catch (JDOMException e) {
     96             e.printStackTrace();
     97         } catch (IOException e) {
     98             e.printStackTrace();
     99         }
    100     }
    101 
    102 }

    注意:

    ❤关于 JDOM 使用过程中 JAR 包的引用 :

    方式1:通过右击项目-->build path-->add external archives...-->然后选择本地文件的jar包
    这种方式并不能将jar包真正导入到项目源码中,当把项目导出放在另外的机器上,这个jar包并不会随着project一同被导出。

    如图:

    方式2:项目根目录下新建lib文件夹——复制、粘帖jar包——右击jar包选择“build path构建路径”——“add to build path添加至构建路径”即可

    如图:

      # DOM4J 方式解析 XML 

      DOM4J 是第三方提供的解析XML方法,需要dom4j-1.6.1.jar包

    示例:

     1 package com.study.dom4jtest;
     2 
     3 import java.io.File;
     4 import java.util.Iterator;
     5 import java.util.List;
     6 
     7 import org.dom4j.Attribute;
     8 import org.dom4j.Document;
     9 import org.dom4j.DocumentException;
    10 import org.dom4j.Element;
    11 import org.dom4j.io.SAXReader;
    12 
    13 /**
    14  * DOM4J 方式解析XML
    15  */
    16 public class DOM4JTest {
    17 
    18     public static void main(String[] args) {
    19         // 解析books.xml文件
    20         // 创建SAXReader的对象reader
    21         SAXReader reader = new SAXReader();
    22         try {
    23             // 通过reader对象的read方法加载books.xml文件,获取docuemnt对象。
    24             Document document = reader.read(new File("books.xml"));
    25             // 通过document对象获取根节点bookstore
    26             Element bookStore = document.getRootElement();
    27             // 通过element对象的elementIterator方法获取迭代器
    28             Iterator it = bookStore.elementIterator();
    29             // 遍历迭代器,获取根节点中的信息(书籍)
    30             while (it.hasNext()) {
    31              System.out.println("=====开始遍历某一本书=====");
    32              Element book = (Element) it.next();
    33              // 获取book的属性名以及 属性值
    34              List<Attribute> bookAttrs = book.attributes();
    35              for (Attribute attr : bookAttrs) {
    36               System.out.println("属性名:" + attr.getName() + "--属性值:" + attr.getValue());
    37              }
    38              //解析子节点的信息
    39              Iterator itt = book.elementIterator();
    40              while (itt.hasNext()) {
    41               Element bookChild = (Element) itt.next();
    42               System.out.println("节点名:" + bookChild.getName() + "--节点值:" + bookChild.getStringValue());
    43              }
    44              System.out.println("=====结束遍历某一本书=====");
    45             }
    46 
    47         } catch (DocumentException e) {
    48             e.printStackTrace();
    49         }
    50     }
    51 
    52 }

    五、四种解析方式比较分析

    基础方法:DOM(平台无关的官方解析方式)、SAX(基于事件驱动的解析方式)
    扩展方法:JDOM、DOM4J(在基础的方法上扩展出的,只有在java中能够使用的解析方法)

    ##解析速度的分析 

    XML四种解析方式性能测试:
    SAX>DOM>DOM4J>JDOM

    JUnit是Java提供的一种进行单元测试的自动化工具。测试方法可以写在任意类中的任意位置。使用JUnit可以没有main()入口进行测试。
    DOM4J在灵活性和对复杂xml的支持上都要强于DOM
    DOM4J的应用范围非常的广,例如在三大框架的Hibernate中是使用DOM4J的方式解析文件的。
    DOM是w3c组织提供的一个官方解析方式,在一定程度上是有所应用的。
    当XML文件比较大的时候,会发现DOM4J比较好用
    1.JUnit:Java提供的单元测试;@Test注解;采用JUnit不需要程序入口main方法
    2.性能测试结果:几kB的xml文件;建议使用DOM4J解析
      DOM-33ms
      SAX-6ms
      JDOM-69ms
      DOM4J-45ms
    工程右键build path --Add library--JUnit单元测试 --version:JUnit4
      DOM:33,SAX:6
      JDOM:69;DOM4J:45
      DOM 有可能溢出
    多使用DOM4J

  • 相关阅读:
    侧边工具栏
    proguard 使用说明
    人员组成
    google play
    大数据平台相关
    HttpClient
    库克
    html5 开发 android 注意点
    htm5 动画制作 工具
    JAVA取得WEBROOT物理路径
  • 原文地址:https://www.cnblogs.com/Qian123/p/5231303.html
Copyright © 2020-2023  润新知