天天看點

Java進階特性 第15節 解析XML文檔(3) - JDOM和DOM4J技術

一、JDOM解析

  特征:

  1、僅使用具體類,而不使用接口。

  2、API大量使用了Collections類。

  Jdom由6個包構成:

  • Element類表示XML文檔的元素
  • org.jdom:      解析xml檔案所要用到的基礎類
  • org.jdom.adapters:   包含DOM适配的Java類
  • org.jdom.filter:      包含xml文檔的過濾類
  • org.jdom.input:     包含讀取XML文檔的Java類
  • org.jdom.output:      包含輸出XML文檔的類
  • org.jdom.trans form: 包含将Jdom xml文檔接口轉換為其他XML文檔接口的Java類

  以下是執行個體:

  • 下載下傳jdom包,解壓檔案jdom-2.0.6.jar,将包導入項目。

  

Java進階特性 第15節 解析XML文檔(3) - JDOM和DOM4J技術
  • 使用jdom建立一個xml檔案,名字為people.xml
package cn.XmlFile;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;

//生成xml檔案
public class CreateJdom {

    public static void main(String[] args) {
        //定義元素
        Element people,student;
        people = new Element("people");
        student = new Element("student");
        //設定屬性
        student.setAttribute("name", "張三");
        student.setAttribute("salary","8000");
        //設定文本
        student.setText("呵呵");
        //将其添加到根目錄下
        people.addContent(student);

        //建立一個文檔。
        Document doc = new Document(people);
        //讀取格式,指派給目前的Format
        Format format = Format.getCompactFormat();
        //對目前格式進行初始化
        format.setEncoding("UTF-8");
        //設定xml檔案縮進4個空格
        format.setIndent("    ");
        //建一個xml輸出工廠,将格式給工廠
        XMLOutputter xmlout = new XMLOutputter(format);
        try {
            //将其寫好的文本給工廠,并且建一個檔案輸出流,将資料輸出
            xmlout.output(doc, new FileOutputStream("D:\\MyPractice01\\sources\\people.xml"));
            System.out.println("成功!");
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}      

   

Java進階特性 第15節 解析XML文檔(3) - JDOM和DOM4J技術
  • 使用Jdom解析people.xml檔案
package cn.XmlFile;

import java.io.IOException;
import java.util.List;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;

//讀取people.xml文檔
public class ReadxmlByJDOM {

    public static void main(String[] args) {
        //建立構造器解析xml
        SAXBuilder sax = new SAXBuilder();
        //建一個文檔去接受資料
        Document doc;
        try {
            //擷取people.xml文檔
            doc = sax.build("D:\\MyPractice01\\sources\\people.xml");
            //獲得根節點
            Element people = doc.getRootElement();
            //獲得根節點下的節點資料
            List<Element> list = people.getChildren();
            for(int i = 0;i<list.size();i++){
                Element e = list.get(i);
                //獲得屬性值
                System.out.println("name:"+e.getAttributeValue("name")+"   salary:"+e.getAttributeValue("salary"));
                //獲得文本值
                System.out.println(e.getText());
            }
        } catch (JDOMException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}      

  

Java進階特性 第15節 解析XML文檔(3) - JDOM和DOM4J技術

二、DOM4J解析

  DOM4J是dom4j.org出品的一個開源XML解析包,它應用于Java平台,采用了Java集合架構并完全支援DOM,SAX和JAXP。 

  DOM4J使用起來非常簡單。隻要你了解基本的XML-DOM模型,就能使用。DOM4J最大的特色是使用大量的接口,這也是它被認為比JDOM靈活的主要原因。 

  特征:

  • JDOM的一種智能分支,它合并了許多超出基本XML文檔表示的功能。
  • 它使用接口和抽象基本類方法。
  • 具有性能優異、靈活性好、功能強大和極端易用的特點。
  • 是一個開放源碼的檔案。

  它的主要接口都在org.dom4j這個包裡定義:

  

Java進階特性 第15節 解析XML文檔(3) - JDOM和DOM4J技術

  1、讀取并解析XML文檔: 

  讀寫XML文檔主要依賴于org.dom4j.io包,其中提供DOMReader和SAXReader兩類不同方式,而調用方式是一樣的。這就是依靠接口的好處。

// 從檔案讀取XML,輸入檔案名,傳回XML文檔
public Document read(String fileName) throws MalformedURLException, DocumentException {
   SAXReader reader = new SAXReader();
   Document document = reader.read(new File(fileName));
   return document;
}
           

  其中,reader的read方法是重載的,可以從InputStream, File, Url等多種不同的源來讀取。得到的Document對象就帶表了整個XML。 

  注意,讀取的字元編碼是按照XML檔案頭定義的編碼來轉換。如果遇到亂碼問題,注意要把各處的編碼名稱保持一緻即可。 

  2、 取得Root節點 

  讀取後的第二步,就是得到Root節點,一切XML分析都是從Root元素開始的。

public Element getRootElement(Document doc){
   return doc.getRootElement();
}
           

  3、 周遊XML樹 

  DOM4J提供至少3種周遊節點的方法: 

  1) 枚舉(Iterator)

// 枚舉所有子節點
for ( Iterator i = root.elementIterator(); i.hasNext(); ) {
   Element element = (Element) i.next();
   // do something
}
// 枚舉名稱為foo的節點
for ( Iterator i = root.elementIterator(foo); i.hasNext();) {
   Element foo = (Element) i.next();
   // do something
}
// 枚舉屬性
for ( Iterator i = root.attributeIterator(); i.hasNext(); ) {
   Attribute attribute = (Attribute) i.next();
   // do something
}
           

  2)遞歸 

  遞歸也可以采用Iterator作為枚舉手段,但文檔中提供了另外的做法。  

public void treeWalk() {
   treeWalk(getRootElement());
}
public void treeWalk(Element element) {
   for (int i = 0, size = element.nodeCount(); i < size; i++)     {
       Node node = element.node(i);
       if (node instanceof Element) {
          treeWalk((Element) node);
       } else { // do something....
       }
   }
} 
           

  3) Visitor模式 

  最令人興奮的是DOM4J對Visitor的支援,這樣可以大大縮減代碼量,并且清楚易懂。了解設計模式的人都知道,Visitor是GOF設計模式之一。其主要原理就是兩種類互相保有對方的引用,并且一種作為Visitor去通路許多Visitable。我們來看DOM4J中的Visitor模式(快速文檔中沒有提供) 。

  隻需要自定一個類實作Visitor接口即可。  

public class MyVisitor extends VisitorSupport { 
  public void visit(Element element){ 
    System.out.println(element.getName()); 
  } 
  public void visit(Attribute attr){ 
    System.out.println(attr.getName()); 
  } 
}
           

  調用:root.accept(new MyVisitor())

  Visitor接口提供多種Visit()的重載,根據XML不同的對象,将采用不同的方式來通路。上面是給出的Element和Attribute的簡單實作,一般比較常用的就是這兩個。VisitorSupport是DOM4J提供的預設擴充卡,Visitor接口的Default Adapter模式,這個模式給出了各種visit(*)的空實作,以便簡化代碼。注意,這個Visitor是自動周遊所有子節點的。如果是root.accept(MyVisitor),将周遊子節點。

  4、 XPath支援 

  DOM4J對XPath有良好的支援,如通路一個節點,可直接用XPath選擇。

public void bar(Document document) {
    List list = document.selectNodes( //foo/bar );
    Node node = document.selectSingleNode(//foo/bar/author);
    String name = node.valueOf( @name );
}
           

  例如,如果你想查找XHTML文檔中所有的超連結,下面的代碼可以實作。

public void findLinks(Document document) throws DocumentException {
    List list = document.selectNodes( //a/@href );
    for (Iterator iter = list.iterator(); iter.hasNext(); ) {
        Attribute attribute = (Attribute) iter.next();
        String url = attribute.getValue();
    }
}
           

  5、 字元串與XML的轉換 

// XML轉字元串
Document document = ...;
String text = document.asXML();
// 字元串轉XML
String text = James ;
Document document = DocumentHelper.parseText(text);
           

  6 、用XSLT轉換XML

public Document styleDocument(
       Document document,
       String stylesheet
    ) throws Exception {
    // load the transformer using JAXP
    TransformerFactory factory = TransformerFactory.newInstance();
    Transformer transformer = factory.newTransformer(
       new StreamSource( stylesheet )
    );
    // now lets style the given document
    DocumentSource source = new DocumentSource( document );
    DocumentResult result = new DocumentResult();
    transformer.transform( source, result );
    // return the transformed document
    Document transformedDoc = result.getDocument();
    return transformedDoc;
}
           

  7、 建立XML 

  一般建立XML是寫檔案前的工作,這就像StringBuffer一樣容易。

public Document createDocument() {
   Document document = DocumentHelper.createDocument();
   Element root = document.addElement(root);
   Element author1 =
       root
          .addElement(author)
          .addAttribute(name, James)
          .addAttribute(location, UK)
          .addText(James Strachan);
   Element author2 =
       root
          .addElement(author)
          .addAttribute(name, Bob)
          .addAttribute(location, US)
          .addText(Bob McWhirter);
   return document;
}
           

  8、檔案輸出 

  一個簡單的輸出方法是将一個Document或任何的Node通過write方法輸出。

FileWriter out = new FileWriter( foo.xml );
document.write(out);
           

  如果你想改變輸出的格式,比如美化輸出或縮減格式,可以用XMLWriter類。

public void write(Document document) throws IOException {
   // 指定檔案
   XMLWriter writer = new XMLWriter(
       new FileWriter( output.xml )
   );
   writer.write( document );
   writer.close();
   // 美化格式
   OutputFormat format = OutputFormat.createPrettyPrint();
   writer = new XMLWriter( System.out, format );
   writer.write( document );
   // 縮減格式
   format = OutputFormat.createCompactFormat();
   writer = new XMLWriter( System.out, format );
   writer.write( document );
}
           

  9、操作XML文檔

  1)建立一個XML文檔

/**
     * 建立一個XML文檔,文檔名由輸入屬性決定
     * @param filename 需建立的檔案名
     * @return 傳回操作結果, 0表失敗, 1表成功
     */
    public int createXMLFile(String filename){
       /** 傳回操作結果, 0表失敗, 1表成功 */
       int returnValue = 0;
       /** 建立document對象,定義一個XML文檔對象。*/
       Document document = DocumentHelper.createDocument();
       /** 建立XML文檔的根books */
       Element booksElement = document.addElement("books");
       /** 加入一行注釋 */
       booksElement.addComment("This is a test for dom4j, holen, 2004.9.11");
       /** 加入第一個book節點,這句定義一個XML元素,這裡添加的是根節點*/
       Element bookElement = booksElement.addElement("book");
       /** 加入show屬性内容 */
       bookElement.addAttribute("show","yes");
       /** 加入title節點 */
       Element titleElement = bookElement.addElement("title");
       /** 為title設定内容 */
       titleElement.setText("Dom4j Tutorials");

       /** 類似的完成後兩個book */
       bookElement = booksElement.addElement("book");
       bookElement.addAttribute("show","yes");
       titleElement = bookElement.addElement("title");
       titleElement.setText("Lucene Studing");
       bookElement = booksElement.addElement("book");
       bookElement.addAttribute("show","no");
       titleElement = bookElement.addElement("title");
       titleElement.setText("Lucene in Action");

       /** 加入owner節點 */
       Element ownerElement = booksElement.addElement("owner");
       ownerElement.setText("O'Reilly");

       try{
           /** 将document中的内容寫入檔案中。通過XMLWriter生成實體檔案,預設生成的XML檔案排版格式比較亂,可以通過OutputFormat類的createCompactFormat()方法或createPrettyPrint()方法
       * 格式化輸出,預設采用createCompactFormat()方法,顯示比較緊湊 
       */
           XMLWriter writer = new XMLWriter(new FileWriter(new File(filename)));
           writer.write(document);
           writer.close();
           /** 執行成功,需傳回1 */
           returnValue = 1;
       }catch(Exception ex){
           ex.printStackTrace();
       }

       return returnValue;
    }      

  2)修改XML檔案中内容

/**
     * 修改XML檔案中内容,并另存為一個新檔案
     * 重點掌握dom4j中如何添加節點,修改節點,删除節點
     * @param filename 修改對象檔案
     * @param newfilename 修改後另存為該檔案
     * @return 傳回操作結果, 0表失敗, 1表成功
     */
    public int ModiXMLFile(String filename,String newfilename){
       int returnValue = 0;
       try{
           SAXReader saxReader = new SAXReader();
           Document document = saxReader.read(new File(filename));
           /** 修改内容之一: 如果book節點中show屬性的内容為yes,則修改成no */
           /**通過xpath查找到相應内容。通過setValue()、setText()修改節點内容。通過remove()删除節點或屬性。*/      
List list = document.selectNodes("/books/book/@show" );
           Iterator iter = list.iterator();
           while(iter.hasNext()){
              Attribute attribute = (Attribute)iter.next();
              if(attribute.getValue().equals("yes")){
                  attribute.setValue("no");
              }  
           }

       /**
     * 修改内容之二: 把owner項内容改為Tshinghua
        * 并在owner節點中加入date節點,date節點的内容為2004-09-11,還為date節點添加一個屬性type
        */
       list = document.selectNodes("/books/owner" );
       iter = list.iterator();
       if(iter.hasNext()){
          Element ownerElement = (Element)iter.next();
          ownerElement.setText("Tshinghua");
          Element dateElement = ownerElement.addElement("date");
          dateElement.setText("2004-09-11");
          dateElement.addAttribute("type","Gregorian calendar");
       }

       /** 修改内容之三: 若title内容為Dom4j Tutorials,則删除該節點 */
       list = document.selectNodes("/books/book");
       iter = list.iterator();
       while(iter.hasNext()){
          Element bookElement = (Element)iter.next();
          Iterator iterator = bookElement.elementIterator("title");
          while(iterator.hasNext()){
              Element titleElement=(Element)iterator.next();
              if(titleElement.getText().equals("Dom4j Tutorials")){
                 bookElement.remove(titleElement);
              }
          }
       }         

       try{
          /** 将document中的内容寫入檔案中 */
          XMLWriter writer = new XMLWriter(new FileWriter(new File(newfilename)));
          writer.write(document);
          writer.close();
          /** 執行成功,需傳回1 */
          returnValue = 1;
       }catch(Exception ex){
          ex.printStackTrace();
       }

   }catch(Exception ex){
       ex.printStackTrace();
   }
   return returnValue;
}      

  3)格式化輸出和指定編碼

  預設的輸出方式為緊湊方式,預設編碼為UTF-8,但對于我們的應用而言,一般都要用到中文,并且希望顯示時按自動縮進的方式的顯示,這就需用到OutputFormat類。

/**
 * 格式化XML文檔,并解決中文問題
 * @param filename
 * @return
 */
public int formatXMLFile(String filename){
   int returnValue = 0;
   try{
       SAXReader saxReader = new SAXReader();
       Document document = saxReader.read(new File(filename));
       XMLWriter writer = null;
       /** 格式化輸出,類型IE浏覽一樣 */
       OutputFormat format = OutputFormat.createPrettyPrint();
       /** 指定XML編碼 */
       format.setEncoding("GBK");
       writer= new XMLWriter(new FileWriter(new File(filename)),format);
       writer.write(document);
       writer.close();     
       /** 執行成功,需傳回1 */
       returnValue = 1;    
   }catch(Exception ex){
       ex.printStackTrace();
   }
   return returnValue;
}      

  以下是執行個體代碼:

  student.xml:

<?xml version="1.0" encoding="utf-8"?>
<students>
    <student age="25">
        <name>張三</name>
        <college>資訊學院</college>
        <telphone>13610262187</telphone>
        <notes>男,1982年生,碩士,現就讀于北京郵電大學</notes>
    </student>
    <student >
        <name>李四</name>
        <college leader="leader">PC學院</college>
        <telphone>13610262187</telphone>
        <notes>男,1983年生,碩士,現就讀于中國農業大學</notes>
    </student>

</students>      

  StudentDom4j.java:

package cn.DOM4JDemo;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import java.util.List;

import org.dom4j.DocumentException;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.dom4j.Document;
import org.dom4j.Element;

/*
 * 使用Dom4j解析XML文檔
 */
public class StudentDom4j {
    private Document document;
    public void getDom(File file){
        //建立SAXReader creates a DOM4J tree from SAX parsing events. 建立SAX解析器
        SAXReader sax=new SAXReader();
        try {
            //生成DOM樹
            document=sax.read(file);
        } catch (DocumentException e) {
            e.printStackTrace();
        }

    }

    //    讀取student.xml檔案中的資訊
    public void showXML(File file){
        //擷取XML的根節點
        Element root=document.getRootElement();
        System.out.println("Root:"+root.getName());

        //擷取所有子元素
        List<Element> childlist=root.elements();
        System.out.println("total child count:"+childlist.size());

        //擷取特定名稱的子元素
        List<Element> student=root.elements("student");
        System.out.println(student.size());
        for(Iterator it=student.iterator();it.hasNext();){
            Element studentele=(Element)it.next();
            String age=studentele.attributeValue("age");
            if(age!=null){
                System.out.println("<"+studentele.getName()+" "+"age="+studentele.attributeValue("age")+">");
            }else{
                System.out.println("<"+studentele.getName()+">");
            }

            //取name的文本
            List<Element> names=studentele.elements("name");
            for(Iterator nameit=names.iterator();nameit.hasNext();){
                Element name=(Element)nameit.next();
                System.out.println("\t"+"name="+name.getText());
            }
            //取college的文本值
            List<Element> colleges=studentele.elements("college");
            for(Iterator collegeit=colleges.iterator();collegeit.hasNext();){
                Element college=(Element)collegeit.next();
                //判斷有沒有屬性,如果有就取屬性值isTextOnly()如果這個元素隻有文本内容則傳回true

                String leader=college.attributeValue("leader");
                String text=college.getText();
                if(leader!=null){
                    System.out.println("\t"+college.getName()+" "+"leader="+leader);
                }else{
                    System.out.println("\t"+"college:"+college.getText());
                }

            }
            //擷取telphone文本值
            List<Element> tel=studentele.elements("telphone");
            for(Iterator telit=tel.iterator();telit.hasNext();){
                Element telele=(Element) telit.next();
                System.out.println("\t"+"telphone:"+telele.getText());
            }

            //擷取notes文本值
            List<Element> notes=studentele.elements("notes");
            for(Iterator noteit=notes.iterator();noteit.hasNext();){
                Element noteele=(Element) noteit.next();
                System.out.println("\tnoteele:"+noteele.getText());
            }
        }



    }
    //儲存
    public void saveXML(File tofile){
        // 以XML格式輸出createPrettyPrint()建立預設的列印格式
        OutputFormat of=OutputFormat.createPrettyPrint();
        //設定編碼
        of.setEncoding("utf-8");
        try {
            //以XML格式輸出到dom4j.xml中
            XMLWriter writer=new XMLWriter(new FileOutputStream(tofile),of);
            //把源樹DOM樹輸出寫進dom4j.xml中
            writer.write(document);
            writer.close();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    //修改XML元素
    public void updateEle(){
        //先要擷取到age
        //得到根元素
        Element root=document.getRootElement();
        //得到 student元素
        List<Element> students=root.elements("student");
        //修改<student age="25">的age屬性為22
        for(Iterator stuit=students.iterator();stuit.hasNext();){
            Element stu=(Element) stuit.next();
            String age=stu.attributeValue("age");
            if(age!=null){
                //就是要替換的,修改屬性值
                stu.attribute("age").setText("22");
            }
            //修改<name>李四</name> 的name文本為sb
            List<Element> names=stu.elements("name");
            for(Iterator name=names.iterator();name.hasNext();){
                Element nameele=(Element) name.next();
                String nametext=nameele.getText();
                if(nametext.equals("李四")){
                    //替換
                    nameele.setText("sb");
                }
            }
            //修改<college leader="leader">的leader值為否
            List<Element> colleges=stu.elements("college");
            for(Iterator collegeit=colleges.iterator();collegeit.hasNext();){
                Element college=(Element) collegeit.next();
                String leader=college.attributeValue("leader");
                if(leader!=null){
                    //替換
                    college.attribute("leader").setText("否");
                }
            }
        }
    }

    //新增元素
    public void addEle(){
    /* <student >
        <name>杜和雨</name>
        <college leader="leader">PC學院</college>
    </student>*/
        //建立根節點
        Element root=document.getRootElement();
        //建立student标簽
        Element student=root.addElement("student");
        //建立student子标簽name
        Element name=student.addElement("name");
        //給name添加文本
        name.setText("杜和雨");
        //建立student的子标簽college
        Element college=student.addElement("college");
        //給college添加文本值
        college.addAttribute("leader", "big boss");

    }
    //    删除元素
    public void deleteEle(){
        /* <student >
            <name>李四</name>
            <college leader="leader">PC學院</college>
            <telphone>13610262187</telphone>
            <notes>男,1983年生,碩士,現就讀于中國農業大學</notes>
        </student>*/

        //得到 根節點
        Element root=document.getRootElement();
        //找到 student節點
        List<Element> students=root.elements();
        //删除第1個student節點的所有子節點
        //students.remove(0);

        //删除college的屬性leader
        for(Iterator<Element> stuit=students.iterator();stuit.hasNext();){
            Element stu=stuit.next();
            List<Element> colleges=stu.elements();
            for(Iterator<Element> collit=colleges.iterator();collit.hasNext();){
                Element coll=collit.next();
                String leader=coll.attributeValue("leader");
                if(leader!=null){
                    coll.remove(coll.attribute("leader"));
                }
            }
        }

    }

    //    主函數,測試
    public static void main(String[] args){
        StudentDom4j sd=new StudentDom4j();
        File file=new File("D:\\MyPractice01\\sources\\student.xml");
        File tofile=new File("D:\\MyPractice01\\sources\\dom4j_student.xml");
        sd.getDom(file);
        sd.showXML(file);
        sd.updateEle();
        sd.addEle();
        sd.deleteEle();
        sd.saveXML(tofile);
    }

}      

   
Java進階特性 第15節 解析XML文檔(3) - JDOM和DOM4J技術

三、總結

  DOM4J性能最好,連Sun的JAXM也在用DOM4J。目前許多開源項目中大量采用DOM4J,例如大名鼎鼎的Hibernate也用DOM4J來讀取XML配置檔案。如果不考慮可移植性,那就采用DOM4J。

      JDOM和DOM在性能測試時表現不佳,在測試10M文檔時記憶體溢出。在小文檔情況下還值得考慮使用DOM和JDOM。雖然JDOM的開發者已經說明他們期望在正式發行版前專注性能問題,但是從性能觀點來看,它确實沒有值得推薦之處。另外,DOM仍是一個非常好的選擇。DOM實作廣泛應用于多種程式設計語言。它還是許多其它與XML相關的标準的基礎,因為它正式獲得W3C推薦(與基于非标準的Java模型相對),是以在某些類型的項目中可能也需要它(如在JavaScript中使用DOM)。

      SAX表現較好,這要依賴于它特定的解析方式-事件驅動。一個SAX檢測即将到來的XML流,但并沒有載入到記憶體(當然當XML流被讀入時,會有部分文檔暫時隐藏在記憶體中)。

轉載于:https://www.cnblogs.com/yutianbao/p/10779723.html