第12章 XML开发

XML全称Extensible Markup Language,可翻译为可扩展标记语言或可延伸标识语言,是一种置标语言。常用XML文档描述数据,使得数据能被多个程序共享,还常用XML文档存放程序的配置参数,也用于定义Web网页上的文档元素(如RSS)以及商业文档。

XML前身是SGML(The Standard Generalized Markup Language),是自IBM从20世纪60年代就开始发展的GML(Generalized Markup Language)。在Java应用中可以方便地引入XML,已经开发了诸多的规范和类库。最为流行的是SAX与DOM。本章深入介绍HTML网页文件与XML文件相互转化、运用DOM与JDOM以及SAX处理XML文档等。使读者轻松掌握XML的用途及规范。

实例103 HTML文件转成XML文件

在现实应用中,由于浏览器的易错性,大量的HTML网页格式很不规范。将HTML网页文件转换成XML文件后,将大大方便网络信息的抓取和维护。本实例介绍如何将HTML网页文件转成XML文件以及需要掌握的规则。需要到官方网站http://sourceforge.net/projects/jtidy/下载开发用的jar文件。文件下载解压缩后,将build文件夹下的Tidy.jar包放入项目的类库中。

技术要点

将HTML网页文件转成XML文件的技术要点如下:

• 运用Java扩展标准库类org.w3c.tidy.Tidy中的方法实现加上URL网页地址的获取和转化。

• 运用输入/输出流缓冲区BufferedInputStream类和FileOutputStream类将HTML文件中的数据读取转化成XML文档。

实现步骤

(1)新建一个类名为TextHTMLToXML.java。

(2)代码如下所示:

package com.zf.s12;                             //创建一个包
import java.io.BufferedInputStream;             //引入类
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.net.URL;
import org.w3c.tidy.Tidy;
public class TextHTMLToXML {                    //操作将HTML文件转成XML文件的类
    private String url;                         //HTML网页地址
    private String outFileName;                 //xml文件所在路径
    private String errOutFileName;              //错误信息文件所在路径
    public TextHTMLToXML(String url,String outFileName,String errOutFileName){
        this.url=url;
        this.outFileName=outFileName;
        this.errOutFileName=errOutFileName;
    }
    public void convert(){                      //转换方法
        URL u;
        BufferedInputStream in;
        FileOutputStream out;
        Tidy tidy=new Tidy();
        tidy.setXmlOut(true);                   //通知Tidy将HTML转成XML
        try {
                                                //将错误信息保存到文件中
              tidy.setErrout(new PrintWriter(new FileWriter(errOutFileName),true));
              u=new URL(url);                   //根据网址创建URL对象
              in=new BufferedInputStream(u.openStream());   //创建缓存输入流
              out=new FileOutputStream(outFileName);        //创建文件输出流
              tidy.parse(in,out);                           //转换文件
              in.close();                                   //释放资源
              out.close();                                  //释放资源
        } catch (Exception e) {                             //捕获异常
              System.out.println(e.getMessage());
        }
    }
    public static void main(String []args){                 //Java程序主入口处
        String htmlFile="http://localhost:8080/Demo/index.jsp";
        String xmlFile="F:/poem.xml";
        String errorFile="F:/error.txt";
                                                            //带参数实例化对象
        TextHTMLToXML htmlToXml=new TextHTMLToXML(htmlFile,xmlFile,errorFile);
        htmlToXml.convert();                                //调用方法进行转换
        System.out.println("HTML文件转换成XML文件结束");
    }
}

(3)运行结果如下所示:

HTML文件
转化成XML文件

源程序解读

(1)实例中设置输出的文件是XML格式,告知Tidy将HTML转换为XML。JTidy提供HTML语法检查和HTML的pretty printing,它还允许将一个HTML文件作为输入,然后将其转换为XML。JTidy读取输入文件,如果发现有任何不匹配或遗漏的闭合标记,将纠正这些标记。最后输出一个格式良好的XML文档。

(2)JTidy对文件转换的方法进行了良好的包装,读者只需获得相应的输入/输出流调用parse()方法便可将HTML文件转换为XML文件。如果转换失败,JTidy会将产生的错误信息写入传入的错误文件中。

实例104 XML文件转成HTML文件

为了得到HTML文档输出,需要将生成的XML文件和控制XML数据的XSL模板相结合。单独的XML文件只能存储数据方式的数据,不能运用样式表对数据进行深加工。本实例介绍运用XSL技术结合XML生成有样式的HTML文档。

技术要点

将XML文件转成HTML网页文件的技术要点如下:

• XML用于承载数据,而XSL则用于设置数据的格式。XSL声明与XML声明的不同之处在于,XML声明只写一行,而且没有结束标签,而XSL声明必须包含结束标签,该标签表示XSL样式表结束:</xsl:stylesheet>。

• XSL专门用于处理XML文档,并且遵循XML语法。它只能在支持XML的应用程序中与XML结合使用。最适合使用XML和XSL的情况是:Web门户、新闻聚合器、社区网站或其他任何需要向各种设备及大量客户端提供信息的We b应用程序。

• XSL不能代替或补充CSS。它不应用于设置HTML样式。但可以将其用于需要频繁重新设计可视化效果、频繁更换布局以及以灵活格式处理数据的网站。

实现步骤

(1)新建一个类名为TextXMLToHTML.java。

(2)代码如下所示:

package com.zf.s12;                                //创建一个包
import java.io.FileInputStream;                    //引入类
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
public class TextXMLToHTML { //操作XML文档转换成HTML文档的类 public static void translate() throws Exception{//将XML文档转成HTML文档的类 FileInputStream fis = new FileInputStream("F:/123.xml");//创建xml文件输入流 Source source = new StreamSource(fis); FileInputStream fis1 = new FileInputStream("F:/123.xsl");//创建xsl文件输入流 Source template = new StreamSource(fis1); Result result = new StreamResult(System.out);//将转换后的结果输出到控制台 Transformer transformer = TransformerFactory.//根据XSL文件创建转换对象 newInstance().newTransformer(template); transformer.transform(source, result); //处理xml进行变换 fis1.close(); //关闭xsl文件输入流 fis.close(); //关闭xml文件输入流 } public static void main(String []args){ //Java程序主入口处 try { translate(); //调用方法将XML文件转换成HTML文件 } catch (Exception e) { //捕获异常 System.out.println("XML转换成HTML失败:"+e.getMessage()); } } }

F:/123.xml

<?xml version="1.0" encoding="GB2312" standalone="no"?>
<employees>
    <employee-list>
          <employee>
              <id>001</id>
              <name>李达</name>
              <gender>男</gender>
              <address>北京海淀</address>
          </employee>
          <employee>
              <id>002</id>
              <name>赵超</name>
              <gender>男</gender>
              <address>上海黄浦</address>
          </employee>
          <employee>
              <id>003</id>
              <name>张云</name>
              <gender>女</gender>
              <address>山东济南</address>
          </employee>
    </employee-list>
</employees>

F:/123.xsl

<?xml version="1.0" encoding="GB2312"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="GB2312"/>
<xsl:template match="/">
<html>
    <head>
          <title>员工信息</title>
    </head>
    <body>
          <tablewidth"800px" border="1" bordercolor="black" style="border-collapse: collapse">
              <tr>
                    <td align="center">编号</td>
                    <td align="center">姓名</td>
                    <td align="center">性别</td>
                    <td align="center">地址</td>
              </tr>
              <xsl:for-each select="employees/employee-list/employee">
              <tr>
                    <td align="center"><xsl:value-of select="id" /></td>
                    <td align="center"><xsl:value-of select="name" /></td>
                    <td align="center"><xsl:value-of select="gender" /></td>
                    <td align="center"><xsl:value-of select="address" /></td>
              </tr>
              </xsl:for-each>
          </table>
    </body>
</html>
</xsl:template>
</xsl:stylesheet>

(3)运行结果如下所示:

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=GB2312">
<title>员工信息</title>
</head>
<body>
<table width="800px" border="1" bordercolor="black" style="border-collapse: collapse">
<tr>
<td align="center">编号</td><td align="center">姓名</td>
<td align="center">性别</td><td align="center">地址</td>
</tr>
<tr>
<td align="center">001</td><td align="center">李达</td>
<td align="center">男</td><td align="center">北京海淀</td>
</tr>
<tr>
<td align="center">002</td><td align="center">赵超</td>
<td align="center">男</td><td align="center">上海黄浦</td>
</tr>
<tr>
<td align="center">003</td><td align="center">张云</td>
<td align="center">女</td><td align="center">山东济南</td>
</tr>
</table>
</body>
</html>

源程序解读

(1)translate()方法创建XML文档输入流,封装XML文档输入流生成XML文档源流对象;创建XSL文档输入流,封装XSL文档输入流生成XSL文档源流对象。根据XSL文档源流对象创建转换器对象,调用转换器对象的transform()方法将XML文档源流进行变换,在控制台输出相应的HTML文档。

(2)生成的result结果输出流对象的参数可以是System.out输出到控制台,也可以将生成的HTML文档保存到指定路径的文件中。

实例105 DOM4j解析XML文件

DOM4j是sourceforge.net上的一个开源项目,主要用于对XML的解析,是一种处理XML文件的技术,通过DOM4j能够读取、修改以及删除XML文档中的内容。本实例将介绍用DOM4j处理XML文档,包括对XML文件的读取、修改节点和属性、删除节点和属性、格式化XML文件以及将读取的XML文件中的内容保存到另一个XML文档中。需要到官方网站http://sourceforge.net/projects/dom4j下载开发用的jar文件。文件下载解压缩后,将相应的jar包放入项目的类库中。

技术要点

使用DOM4j解析XML文件的技术要点如下:

• DOM4j是一个易用的、开源的库,用于XML、XPath和XSLT。它应用于Java平台,采用了Java集合框架并完全支持DOM、SAX以及JAXP。

• 读写XML文档主要依赖于org.dom4j.io包,其中提供DOMReader和SAXReader两类不同方式,而调用方式是一样的。这就是依赖接口的好处。

• Visit是GOF设计模式之一。主要原理是两种类互相保有对方的引用。DOM4j支持Visit模式,可以根据XML不同的对象采用不同的方式来访问。

实现步骤

(1)创建一个类名为TextDom4jParseXML.java。

(2)代码如下所示:

package com.zf.s12;                                    //创建一个包
import java.io.File;                                   //引入类
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
public class TextDom4jParseXML {                      //操作DOM4j处理XML文件的类
    public static int createXMLFile(String filename) {//创建XML文件
          int returnValue = 0;                    //返回操作结果, 0表示失败, 1表示成功
                                                      //创建document对象
          Document document = DocumentHelper.createDocument();
                                                      //创建XML文档的根trees
          Element treesElement = document.addElement("trees");
          treesElement.addComment("This is a test for dom4j");//加入一行注释
                                                      //加入第一个tree节点
          Element treeElement = treesElement.addElement("tree");
          treeElement.addAttribute("id", "001");      //加入id属性内容
          treeElement.addAttribute("name", "rongshu");//加入name属性内容
                                                      //加入description节点
          Element descElement = treeElement.addElement("description");
          descElement.setText("It is a big tree");    //为description设置内容
          treeElement = treesElement.addElement("tree");//类似地完成后两个tree
          treeElement.addAttribute("id", "002");
          treeElement.addAttribute("name", "yangshu");//加入name属性内容
          descElement = treeElement.addElement("description");
          descElement.setText("The tree grows taller from day to day");
          treeElement = treesElement.addElement("tree");
          treeElement.addAttribute("id", "003");      //加入id属性内容
          treeElement.addAttribute("name", "songshu"); //加入name属性内容
          descElement = treeElement.addElement("description");
          descElement.setText("The tree is very beautiful");
                                                      //加入flower节点
          Element ownerElement = treesElement.addElement("flower");
          ownerElement.setText("rose");                //节点内容
          try {
              XMLWriter writer = new XMLWriter(
                    new FileWriter(new File(filename)));//创建XML写对象
              writer.write(document);                   //将document中的内容写入文件中
              writer.close();                           //流关闭
              returnValue = 1;                          //执行成功,需返回1
          } catch (Exception ex) {                      //捕获异常
              ex.printStackTrace();
          }
          return returnValue;
    }
                                                       //将文档内容输出到文件或控制台
    public static void read(Document document) throws IOException {
          XMLWriter writer = new XMLWriter(            //将文档内容输出到文件
                  new FileWriter("F:/output.xml"));
          writer.write(document);                      //将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);
    }
    public static Document getDocument(String fileName) throws IOException,
              DocumentException {                      //获取Document
          SAXReader saxReader = new SAXReader();       //创建对象
          Document document = saxReader.read(
              new File(fileName));                     //读取文件获得Document
          return document;
    }
    public static boolean delete(String fileName, String id) throws IOException,
              DocumentException {                      //根据编号不同进行删除
          boolean returnValue = false;
          Document document = getDocument(fileName);   //调用方法获取Document
          Element root = document.getRootElement();    //获得根节点
          List list = document.selectNodes("/trees/tree/@id");//获得编号列表
          Iterator iter = list.iterator();
          while (iter.hasNext()) {                     //循环获得编号属性信息
              Attribute attribute = (Attribute) iter.next();
              if (attribute.getValue().equals(id)) {   //判断编号是否与指定编号相同
                  Element element = attribute.getParent(); //获得属性的根节点
                  root.remove(element);                //移除节点
              }
          }
          try {
              XMLWriter writer = new XMLWriter(new FileOutputStream(new File(
                        fileName)));                   //将document中的内容写入文件中
              writer.write(document);
              writer.close();
              returnValue = true;                      //执行成功,需返回true
          } catch (Exception ex) {                     //捕获异常
              ex.printStackTrace();
              return returnValue;
          }
          return returnValue;
    }
    public static int update(String fileName, String newName) {
        int returnValue = 0;
        try {
              Document document = getDocument(fileName);
              //tree节点中name属性的内容如果是yangshu,则改为liushu
                                                       //获得name属性列表
              List list = document.selectNodes("/trees/tree/@name");
              Iterator iter = list.iterator();
              while (iter.hasNext()) {                 //循环获得name属性信息
                  Attribute attr = (Attribute) iter.next();
                  if (attr.getValue().equals("yangshu")) { //判断name属性的值
                        attr.setValue(newName);        //重新设置新值
                  }
              }
              //把flower项改为lily,并在flower节点上加入date节点内容:2009-04-10,还为date
                节点添加属性type
              list = document.selectNodes("/trees/flower");   //获得flower节点
              iter = list.iterator();
              if (iter.hasNext()) {                     //循环获得节点信息
                  Element flower = (Element) iter.next();
                  flower.setText("lily");               //重新设置节点名称
                  Element date = flower.addElement("date");//在节点flower上加入date节点
                  date.setText("2009-04-10");           //date节点的内容
                  //在date节点上加入type节点和节点内容
                  date.addAttribute("type", "calendar");
              }
              try {
                  XMLWriter writer = new XMLWriter(new FileWriter(new File(
                            fileName)));                 //将document中的内容写入文件中
                  writer.write(document);
                  writer.close();
                  returnValue = 1;                       //执行成功,需返回1
              } catch (Exception ex) {                   //捕获异常
                  System.out.println("修改XML文件出错:"+ex.getMessage());
                  returnValue=0;
              }
        } catch (Exception e) {                          //捕获异常
              System.out.println("修改XML文件出错:"+e.getMessage());
              returnValue=0;
        }
        return returnValue;
    }
    //格式化XML文档并解决中文问题
    public static int formatXML(String fileName, String encoding) {
        int returnValue = 0;
        try {
              Document document = getDocument(fileName);//调用方法获得Document
              XMLWriter writer = null;
                                                       //格式化输出,类型与ID浏览一样
              OutputFormat format = OutputFormat.createPrettyPrint();
              format.setEncoding(encoding);            //指定XML编码方式
              //将文档内容按指定编码格式输出到文件
              writer = new XMLWriter(new FileWriter(new File(fileName)), format);
              writer.write(document);
              writer.close();
              returnValue = 1;
          } catch (Exception e) {                       //捕获异常
              System.out.println("格式化文档出错:"+e.getMessage());
              returnValue=0;
          }
          return returnValue;
    }
    public static void toControl(String fileName,Document document){
          try {
              XMLWriter writer = new XMLWriter(         //将文档内容输出到文件
                        new FileWriter(fileName));
              writer.write(document);                   //将document中的内容写入文件
              writer.close();
              //将文档内容格式化输出到控制台
              OutputFormat format = OutputFormat.createPrettyPrint();
              writer = new XMLWriter(System.out, format);
              writer.write(document);
          } catch (Exception e) {
              System.out.println("输出出错:"+e.getMessage());
          }
    }
    public static String XmlToString(Document document) {  //将document转化为String
          return document.asXML();
    }
    public static Document StringToDocument(String text) { //将String转化为document
          try {
              return DocumentHelper.parseText(text);
          } catch (DocumentException e) {
              System.out.println("转换出错:"+e.getMessage());
              return null;
          }
    }
    public static void print(String fileName) {       //输出节点
          try {
              Document doc = getDocument(fileName);   //调用方法获得Document
              Node node = doc.selectSingleNode("/trees/tree");
              System.out.println("输出节点:" + node.asXML());//将node转化成String
          } catch (Exception e) {
              System.out.println("输出节点出错:"+e.getMessage());
          }
    }
    public static void main(String[] args) throws Exception { //Java程序主入口处
          String fileName="F:/test.xml";               //不存在的文件
          System.out.println("1.创建XML文档");
          if(createXMLFile(fileName)==1)               //判断是否创建XML文档
              System.out.println("创建XML文档成功");
          else
              System.out.println("创建XML文档失败!!!");
          System.out.println("2.读取XML文档,将文档内容输出到文件或控制台");
          read(getDocument(fileName));                 //调用方法读取文档内容
          System.out.println();
          System.out.println("3.修改XML文档内容,输出修改后的文档");
          update(fileName, "liushu");                  //调用方法修改XML文档
          toControl(fileName,getDocument(fileName));   //输出修改后的XML文档
          System.out.println("4.删除编号为002的节点,输出删除后的文档");
          delete(fileName, "002");
          toControl(fileName,getDocument(fileName));   //输出删除后的XML文档
          System.out.println("5.输出节点(/trees/tree)");
          print(fileName);                             //输出节点
          System.out.println("6.格式化文档解决中文问题");
          if(formatXML(fileName, "GBK")==1)            //判断是否格式化成功
              System.out.println("格式化文档成功");
          else
              System.out.println("格式化文档失败!!!");
    }
}

(3)运行结果如下所示:

1.创建XML文档
创建XML文档成功
2.读取XML文档,将文档内容输出到文件或控制台
<?xml version="1.0" encoding="UTF-8"?>
<trees>
  <!--This is a test for dom4j-->
  <tree id="001" name="rongshu">
    <description>It is a big tree</description>
  </tree>
  <tree id="002" name="yangshu">
    <description>The tree grows taller from day to day</description>
  </tree>
  <tree id="003" name="songshu">
    <description>The tree is very beautiful</description>
  </tree>
  <flower>rose</flower>
</trees>
<?xml version="1.0" encoding="UTF-8"?>
<trees><!--This is a test for dom4j--><tree id="001" name="rongshu"> <description>
It is a big tree</description></tree><tree id="002" name="yangshu"> <description>
The tree grows taller from day to day</description></tree><tree id="003"name=
"songshu"> <description>The tree is very beautiful</description></tree><flower>
rose</flower></trees>
3.修改XML文档内容,输出修改后的文档
<?xml version="1.0" encoding="UTF-8"?>
<trees>
  <!--This is a test for dom4j-->
  <tree id="001" name="rongshu">
    <description>It is a big tree</description>
  </tree>
  <tree id="002" name="liushu">
    <description>The tree grows taller from day to day</description>
  </tree>
  <tree id="003" name="songshu">
    <description>The tree is very beautiful</description>
  </tree>
  <flower>lily
    <date type="calendar">2009-04-10</date>
  </flower>
</trees>
4.删除编号为002的节点,输出删除后的文档
<?xml version="1.0" encoding="UTF-8"?>
<trees>
  <!--This is a test for dom4j-->
  <tree id="001" name="rongshu">
    <description>It is a big tree</description>
  </tree>
  <tree id="003" name="songshu">
    <description>The tree is very beautiful</description>
  </tree>
  <flower>lily
    <date type="calendar">2009-04-10</date>
  </flower>
</trees>
5.输出节点(/trees/tree)
输出节点:<tree id="001" name="rongshu"><description>It is a big tree</description></tree>
6.格式化文档解决中文问题
格式化文档成功

源程序解读

(1)createXMLFile()方法创建XML文档。createDocument()方法创建Document,其中Document定义XML文档,创建XML文档的根节点。addElement()方法是添加节点, addAttribute()方法是添加指定节点的属性,addComment()方法是加入一行注释,setText()方法是添加节点的内容。定义完Document文档需要将Document中的内容通过XML写对象写入文件中。需要注意的是,写完后要释放XML写对象资源。

(2)read()方法读取XML文档通过多种方式显示。通过XML写对象封装文件写对象将XML文档写入到文件中。通过createPrettyPrint()方法将文档内容美化(缩进)格式输出到控制台,通过createCompactFormat()方法将文档内容缩减(紧凑)格式输出到控制台。

(3)getDocument()方法根据传入的文件读取XML,返回XML文档。其中read()方法是重载的,可以从InputStream、File、Url等多种不同的源来读取,得到的Document对象就代表整个XML。读取的字符编码是按照XML文件头定义的编码来转换。

(4)delete()方法根据编号不同删除节点。DOM4j对XPath有良好的支持。如访问一个节点或属性,可直接用XPath选择,如“/trees/tree/@id”是访问id属性。Document文档根据selectNodes()方法获得id属性的列表集合,再循环获得每个id属性的信息。找到指定的id属性,根据getParent()方法获得该属性所在的节点,根据remove()方法移除该节点。对XML文档进行修改后需要重新将Document文档的内容再次写入文件中,以保证XML文档数据的一致性。

(5)update()方法根据具体需求修改XML文档数据。运用XPath访问子节点的name属性,循环获得每个属性信息,根据getValue()方法获得属性的值进行判断,再根据setValue()方法重新设置属性的值。运用XPath访问子节点flower获得子节点的列表集合,循环遍历获得每个子节点的信息,重新设置flower节点的值,在节点上加入date子节点并对date子节点设置值,再在date节点上添加子节点type值并设值。同样由于修改原XML文档的内容,需要重新将Document文档的内容再次写入文件中,以保证XML文档数据的一致性。

(6)formatXML()方法为解决中文问题格式化XML文档。通过setEncoding()方法指定XML编码方式,将文档内容按指定的编码方式输出或保存。

(7)XmlToString()方法将Document文档通过asXML()方法转化成字符串输出。StringTODocument()方法通过Document文档的帮助类的parseText()方法将字符串类型数据转化成Document文档。

(8)print()方法根据Document文档的selectSingleNode()方法运用XPath访问获得节点,再将获得的节点根据asXML()方法转化成字符串输出。

实例106 JDOM解析XML文件

JDOM是一种使用XML的独特Java工具包,用于快速开发XML应用程序,设计包含Java语言的语法乃至语义。本实例介绍如何使用JDOM解析XML文件以及JDOM中的一些规范。需要到官方网站http://www.jdom.org下载开发用的jar文件。文件下载解压缩后,将build文件夹下的jar包放入项目的类库中。

技术要点

使用JDOM解析XML文档的技术要点如下:

• JDOM是用读、写和操作XML文档的API。JDOM利用更为强有力的Java语言的诸多特性,包括方法重载、优先权合和类映射等,把SAX和DOM的功能有效地结合起来。

• JDOM基于树形结构。利用纯Java的技术对XML文档实现解析、生成、序列化以及多种操作。JDOM不允许同一个节点同时被两个或多个文档相关联,要在第2个文档中使用原来老文档中的节点,需要使用detach()方法把这个节点分开。

• JDOM从文件、流、系统ID和URL中得到Document对象。在JDOM中,大多数导航通过Element类的方法进行。每个Element的完整子元素(包括子元素的所有内容,即说明、处理指令、文本节点和元素。要进行深度搜索,就要对当前元素的子元素应用getContent()方法,通常采用递归)在getContent()方法返回java.util.List中提供。getRootElement()方法获得根节点,getChildren()获得所有子节点的一个列表,getChild()方法获得指定名称的第一个子元素,root.getChildren().romove(0)是删除节点的第一个子节点。

实现步骤

(1)创建一个类名为TextJDOMparseXML.java。

(2)代码如下所示:

package com.zf.s12;                        //创建一个包
import java.io.File;                       //引入类
import java.io.FileWriter;
import java.util.Iterator;
import java.util.List;
import org.jdom.*;
import org.jdom.input.SAXBuilder;
import org.jdom.output.*;
public class TextJDOMparseXML {                       //操作使用JDOM解析XML文件的类
    public Document createDocument() {                //生成一个新的Document对象
          Document document = new Document();         //创建一个Document对象
          return document;
    }
    public Element createElement(String paramName, String paramValue) {
          Element element = new Element(paramName);   //创建节点对象
          element.setText(formatTextForXML(paramValue)); //设置节点的值并格式化字符串
          return element;
    }
    public int createXMLFile(String fileName, String encoding) {//创建XML文件
          int returnValue = 0;
          Document document = createDocument();                //调用方法创建XML文档
          Element treesElement = createElement("trees", null); //创建根节点
          Element treeElement = new Element("tree");           //创建节点tree
          treeElement.setAttribute("id", "001");               //节点属性id
          treeElement.setAttribute("name", "rongshu");         //节点属性name
          Element typeElement = new Element("type");           //创建子节点
          typeElement.setText("style");                        //设置子节点内容
          typeElement.setAttribute("id", "xiaoshu");           //设置子节点的属性
          treeElement.addContent(typeElement);                 //添加子节点
          Element descElement = new Element("description");    //创建子节点
          descElement.setText(formatTextForXML("It is a big tree"));//子节点的内容
          treeElement.addContent(descElement);                  //将子节点添加到节点
          treesElement.addContent(treeElement);                 //为根节点添加节点
          Element tree1Element = new Element("tree");
          tree1Element.setAttribute("id", "002");
          tree1Element.setAttribute("name", "yangshu");
          Element type1Element = new Element("type");
          type1Element.setText("style");
          type1Element.setAttribute("id", "dashu");
          tree1Element.addContent(type1Element);
          Element desc1Element = new Element("description");
          desc1Element.setText(formatTextForXML("The tree grows taller from day to day"));
          tree1Element.addContent(desc1Element);
          treesElement.addContent(tree1Element);
Element tree2Element = new Element("tree"); tree2Element.setAttribute("id", "003"); tree2Element.setAttribute("name", "songshku"); Element desc2Element = new Element("description"); desc2Element.setText(formatTextForXML("The tree is very beautiful")); tree2Element.addContent(desc2Element); treesElement.addContent(tree2Element); Element flowerElement = new Element("flower"); flowerElement.setText("rose"); treesElement.addContent(flowerElement); document.addContent(treesElement); //将根节点添加到XML文档 try { saveFile(fileName, document, encoding); //调用方法创建XML文件保存到文件中 returnValue = 1; } catch (Exception e) { //捕获异常 System.out.println("创建XML文件失败:" + e.getMessage()); } return returnValue; } public void read(Document document) { //读取文件 try { Format format = Format.getPrettyFormat();//缩进格式(美化) format.setEncoding("GB2312"); //设置编码方式 //指定XML文件的输出或保存格式 XMLOutputter xmlOut = new XMLOutputter(format); xmlOut.output(document, System.out); //将内容写入文件 } catch (Exception e) { System.out.println("读取XML文件失败:" + e.getMessage()); } } public int update(String fileName,String newName) {//修改XML文档 int returnValue = 0; Document document = getDocument(fileName); //调用方法获得Document文档 List list=document.getRootElement().getChildren();//获得节点列表 for(int i=0;i<list.size();i++){ //循环获得每个节点的信息 Element element=(Element)list.get(i); Attribute attr=element.getAttribute("name");//获得节点的name属性 if(attr!=null){ if(attr.getValue().equals("yangshu")) //判断属性的值 attr.setValue(newName); //重新设置属性的值 } } list=document.getRootElement().getChildren("flower");//获得flower节点 Iterator iter=list.iterator(); if(iter.hasNext()){ //判断节点 Element el=(Element)iter.next(); el.setText("lily"); //重新设置节点的内容 Element date=new Element("date"); //创建date节点 date.setText(formatTextForXML("2009-04-10"));//设置date节点内容 date.setAttribute("type","calendar"); //设置date节点的属性 el.addContent(date); //flower节点添加date节点 } try { //调用方法保存修改后的XML文档并输出 showFile(fileName, document); returnValue=1; } catch (Exception e) { //捕获异常 System.out.println("修改XML文件出错:"+e.getMessage());
} return returnValue; } public boolean delete(String fileName,String id) { //删除指定节点 boolean returnValue = false; Document doc = getDocument(fileName); //调用方法获得Document文档 Element root=doc.getRootElement(); //获得根节点 List list=root.getChildren("tree"); //获得节点tree for(int i=0;i<list.size();i++){ //循环遍历获得每个tree节点 Element element = (Element) list.get(i); //获得每个子节点信息 if (element.getAttributeValue("id").equals(id)) {//判断节点属性 root.removeContent(element); //移除节点 } } try { //调用方法保存修改后的XML文档并输出 showFile(fileName, doc); returnValue=true; } catch (Exception e) { //捕获异常 return returnValue; } return returnValue; } public Document getDocument(String fileName) { //根据指定路径载入XML文件 Document document = null; try { SAXBuilder saxBuilder = new SAXBuilder(); //创建对象 document = saxBuilder.build(new File(fileName));//载入XML文件 } catch (Exception e) { //捕获异常 System.out.println("载入失败:" + e.getMessage()); return null; } return document; } //保存XML文件到指定的路径 public boolean saveFile(String fileName, Document document, String encode) { Format format = Format.getPrettyFormat(); //缩进格式(美化) if ( encode != null ) { format.setEncoding(encode); //设置xml文件的字符集 } format.setIndent(" "); //设置xml文件的缩进 try { XMLOutputter xmlOut = new XMLOutputter(format); FileWriter fileWriter = new FileWriter(fileName); //将XML文件写入指定路径 xmlOut.output(document, fileWriter); //将内容写入文件 } catch ( Exception e ) { return false; } return true; } public void showFile(String fileName,Document document){//显示XML文件内容输出到控制台 Format format = Format.getPrettyFormat(); format.setEncoding("GB2312"); //设置xml文件的编码格式 format.setIndent(" "); //设置xml文件的缩进 try { //指定XML文件的输出或保存格式 XMLOutputter xmlOut = new XMLOutputter(format); FileWriter fileWriter = new FileWriter(fileName);//将XML文件写入指定路径 xmlOut.output(document, fileWriter); //将内容写入文件 xmlOut.output(document, System.out); //将内容输出到控制台 } catch (Exception e) { System.out.println("XML文件输出到控制台出错"); } } //过滤字符串,防止XML文件中出现非法字符 public String formatTextForXML(String sourceString) { if (sourceString == null) { //判断传入字符串是否为空 return null; } int strLen = 0; StringBuffer reString = new StringBuffer(); //创建缓冲字符串 String deString = ""; strLen = sourceString.length(); //获得字符串长度
for (int i = 0; i < strLen; i++) { //对字符串中的元素进行判断 char ch = sourceString.charAt(i); //获得每个元素 switch (ch) { case '<': deString = "<"; break; case '>': deString = ">"; break; case '\"': deString = "\""; break; case '&': deString = "&"; break; case 13: deString = "\n"; break; default: deString = "" + ch; } reString.append(deString); } return reString.toString(); } public static void main(String[] args) { //Java程序主入口处 String fileName = "F:/test1.xml"; TextJDOMparseXML jDomXml = new TextJDOMparseXML(); //实例化对象 System.out.println("1.运用JDOM创建XML文件"); if (jDomXml.createXMLFile(fileName, "GB2312") == 1) System.out.println("运用JDOM创建XML文件成功"); else System.out.println("运用JDOM创建XML文件失败"); System.out.println("2.运用JDOM读取XML文件"); jDomXml.read(jDomXml.getDocument(fileName)); //调方法读取XML文档 System.out.println("3.修改XML文档内容,输出修改后的文档"); jDomXml.update(fileName, "liushu"); //调方法修改XML文档 System.out.println("4.删除编号为002的节点,输出删除后的文档"); jDomXml.delete(fileName, "002"); //调方法删除指定XML文档内容 } }

(3)运行结果如下所示:

1.运用JDOM创建XML文件
  运用JDOM创建XML文件成功
2.运用JDOM读取XML文件
<?xml version="1.0" encoding="GB2312"?>
<trees>
  <tree id="001" name="rongshu">
    <type id="xiaoshu">style</type>
    <description>It is a big tree</description>
  </tree>
  <tree id="002" name="yangshu">
    <type id="dashu">style</type>
    <description>The tree grows taller from day to day</description>
  </tree>
  <tree id="003" name="songshku">
    <description>The tree is very beautiful</description>
  </tree>
  <flower>rose</flower>
</trees>
3.修改XML文档内容,输出修改后的文档
<?xml version="1.0" encoding="GB2312"?>
<trees>
  <tree id="001" name="rongshu">
    <type id="xiaoshu">style</type>
    <description>It is a big tree</description>
  </tree>
  <tree id="002" name="liushu">
    <type id="dashu">style</type>
    <description>The tree grows taller from day to day</description>
  </tree>
  <tree id="003" name="songshku">
    <description>The tree is very beautiful</description>
  </tree>
  <flower>
    lily
    <date type="calendar">2009-04-10</date>
  </flower>
</trees>
4.删除编号为002的节点,输出删除后的文档
<?xml version="1.0" encoding="GB2312"?>
<trees>
  <tree id="001" name="rongshu">
    <type id="xiaoshu">style</type>
    <description>It is a big tree</description>
  </tree>
  <tree id="003" name="songshku">
    <description>The tree is very beautiful</description>
  </tree>
  <flower>
    lily
    <date type="calendar">2009-04-10</date>
  </flower>
</trees>

源程序解读

(1)createDocument()方法创建一个新的Document对象。createElement()方法根据传入参数创建Element节点对象,根据setText()方法为节点内容赋值。

(2)createXMLFile()方法调用createDocument()方法获得Document对象,调用createElement()方法获得根节点。根据setText()方法为节点添加内容,根据setAttribute()方法为节点添加属性,根据addContent()方法将子节点添加到其父节点中。创建Document对象完毕后,调用saveFile()方法将XML文档存到指定文件中。

(3)read()方法中Format的getPrettyFormat()方法是美化缩进,根据setEncoding()方法设置编码方式,再根据指定的输出格式创建XML输出对象。根据output()方法中设置的System.out参数,将XML文档内容输出到控制台。

(4)update()方法根据getChildren()方法获得子节点列表,循环遍历列表获得每个节点的信息,根据getAttribute()方法获得节点中的name属性,判断属性值进行重新设置值。根据getChildren("flower")获得flower节点列表,循环遍历列表获得每个节点信息。根据setText()方法重新设置节点的内容,根据setAttribute()方法设置节点的属性信息,再根据addContent()方法将子节点添加到其父节点中。由于XML文档内容进行了修改需要重新保存XML文档,则调用showFile()方法对XML文档进行保存和输出。

(5)delete()方法根据getChildren("tree")获得tree节点列表,循环遍历节点列表获得每个节点的属性信息,判断属性值获得属性所在的节点,再根据removeContent()方法移除该节点。(6)getDocument()方法创建SAXBuilder对象用来根据指定路径载入XML文件。

(7)saveFile()方法指定XML文档的美化(缩进)格式和编码方式创建XML输出对象,指定XML文档的写入路径创建文件写对象。运用output()方法将内容分别输出到文件中和控制台上。

(8)formatTextForXML()方法为防止XML文件中出现非法字符而过滤字符串。根据字符串的charAt()方法获得字符串中的每个元素,运用流程分支依次判断每个元素。

实例107 DOM解析XML文件

所有类型的XML解析器都要求处理对象是“格式良好”的XML文档,有些还能根据DTD或XML Schema进行有效性验证。DOM是一种处理XML文档的技术,通过DOM能够对XML进行各种操作。本实例介绍如何使用DOM解析XML文件把员工信息读到内存中,并把员工对象的信息保存到另一个XML文档中。

技术要点

运用DOM解析XML文件的技术要点如下:

• DOM(Document Object Model)解析器将XML文档一次性解析,生成一个位于内存中的对象树用以描述该文档。DOM是一种与平台和语言无关的接口,它允许程序和脚本动态访问和修改文档的内容、结构和类型。

• DOM将整个XML文档读到内存中,用基本对象描述XML文档的元素,基本对象之间以树形的结构组织。

实现步骤

(1)创建一个类名为TextDOMParseXML.java。

(2)代码如下所示:

package com.zf.s12;                                    //创建一个包
import java.io.File;                                   //引入类
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.*;
class Employee {                                       //员工类
    private String eId;                                //员工编号
    private String name;                               //员工名称
    private String gender;                             //性别
    private int age;                                   //年龄
    private String address;                            //地址
public String toString(){ StringBuffer sb = new StringBuffer(); sb.append("姓名:").append(this.name).append("; "); sb.append("性别:").append(gender).append("; "); sb.append("年龄:").append(age).append("; "); sb.append("地址:").append(address); return sb.toString(); } public String getEId() { return eId; } public void setEId(String id) { eId = id; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } } class TextDOMParseXML { //用DOM处理XML文档 public static List readXMLFile(String fileName) throws Exception {//读取XML //文件得到DOM解析器的工厂实例 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder build = null; try { build = dbf.newDocumentBuilder(); //从DOM工厂获得DOM解析器 } catch (Exception e) { //捕获异常 System.err.println(e); return null; }
Document doc = null; try { doc = build.parse(fileName); //解析XML文档的Document对象 doc.normalize(); //去掉xml文档中格式化内容空白 } catch (DOMException dom) { //捕获DOM异常 System.err.println(dom.getMessage()); return null; } catch (IOException ioe) { //捕获IO流异常 System.err.println(ioe); return null; }
List employees = new ArrayList(); //创建列表 Employee employee = null; Element root = doc.getDocumentElement(); //得到XML文档的根节点 //取employee元素列表 NodeList nodeList = root.getElementsByTagName("employee"); for (int i = 0; i < nodeList.getLength(); i++) { Element element = (Element) nodeList.item(i);//依次取每个employee元素 employee = new Employee(); //创建一个员工实例 employee.setEId(element.getAttribute("id")); //取员工的编号属性 employee.setGender(element.getAttribute("gender"));//取员工的性别属性 //取员工姓名元素 NodeList names = element.getElementsByTagName("name"); if (names.getLength() == 1) { Element e = (Element) names.item(0); //取姓名元素的第一个子节点,即为姓名的值节点 Text t = (Text) e.getFirstChild(); employee.setName(t.getNodeValue()); //获取值节点的值 } //取年龄元素 NodeList ages = element.getElementsByTagName("age"); if (ages.getLength() == 1) { Element e = (Element) ages.item(0); Text t = (Text) e.getFirstChild(); employee.setAge(Integer.parseInt(t.getNodeValue())); } //取地址元素 NodeList phones = element.getElementsByTagName("address"); if (phones.getLength() == 1) { Element e = (Element) phones.item(0); Text t = (Text) e.getFirstChild(); employee.setAddress(t.getNodeValue()); } employees.add(employee); //将新建的employee加到结果列表 } return employees; //返回结果列表 } //用DOM写XML文档,把学生信息以XML文档的形式存储 public static String writeXMLFile(String outFile, List employees) throws Exception { //得到DOM解析器的工厂实例 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = null; try { db = dbf.newDocumentBuilder(); //从DOM工厂获得DOM解析器 } catch (Exception pce) { //捕获异常 System.err.println(pce); return null; } Document doc = null; doc = db.newDocument(); //新建一个空文档 Element root = doc.createElement("employees");//建立根元素employees并添到文档 doc.appendChild(root); //添加根元素 for (int i = 0; i < employees.size(); i++) { //循环获取员工的信息 Employee employee = (Employee) employees.get(i);//依次取得每个员工的信息 //建立employee元素,有编号和性别属性 Element element = doc.createElement("employee"); element.setAttribute("id", employee.getEId()); //设置编号 element.setAttribute("gender", employee.getGender());//设置性别 root.appendChild(element); //添加到根元素中 Element name = doc.createElement("name");//建立name元素 //将name元素添加到employee元素下面 element.appendChild(name); Text tName = doc.createTextNode(employee.getName());//为name元素赋值 name.appendChild(tName); Element age = doc.createElement("age"); //建立age元素 element.appendChild(age); //将age元素添加在employee元素下 Text tAge = doc.createTextNode( //为age元素赋值 String.valueOf(employee.getAge())); age.appendChild(tAge); Element phone = doc.createElement("address");//建立address元素 //将address元素添加到employee元素下面 element.appendChild(phone); //为address元素赋值 Text tPhone = doc.createTextNode(employee.getAddress()); phone.appendChild(tPhone); } return domDocToFile(doc, outFile, "GB2312"); //把XML文档输出到指定的文件 } //使用JAXP将DOM对象写到XML文档里 public static String domDocToFile(Document doc, String fileName, String encoding) throws TransformerException {//使用JAXP将DOM对象写到XML文档里 //为了得到XSLT引擎创建对象 TransformerFactory tFactory = TransformerFactory.newInstance(); //创建XSLT引擎对象输出XML文档 Transformer transformer = tFactory.newTransformer(); Properties properties = transformer.getOutputProperties();//获得属性对象 properties.setProperty(OutputKeys.ENCODING, encoding);//设置新的编码格式 properties.setProperty(OutputKeys.METHOD, "xml");//设置输出格式为XML transformer.setOutputProperties(properties); DOMSource source = new DOMSource(doc); //创建对象 File file = new File(fileName); //创建文件
StreamResult result = new StreamResult(file);//获得输出对象 StreamResult result1=new StreamResult(System.out);//结果输出到控制台 transformer.transform(source, result); //执行DOM文档到XML文件转换 transformer.transform(source, result1); //文档输出到控制台 System.out.println();//换行 return file.getAbsolutePath(); //将输出文件的路径返回 } public static void main(String[] args) { //Java程序主入口处 String inFileName = "F:/poem.xml"; //读出文件 String outFileName = "F:/poem1.xml"; //写出文件 try { //调用方法获得所有员工的信息 List employees = TextDOMParseXML.readXMLFile(inFileName); System.out.println("输出到控制台的XML文档:"); System.out.println("文件(包括路径和后缀):"+TextDOMParseXML.writeXMLFile (outFileName, employees)); //将数据写到另一个XML文件中 } catch (Exception e) { //捕获异常 e.printStackTrace(); } } }

F:/poem.xml

<?xml version="1.0" encoding="GB2312"?>
<employees>
    <employee id="001" gender = "male">
          <name>susan</name>
          <age>32</age>
          <address>8888888888</address>
    </employee>
    <employee gender = "female">
          <name>tom</name>
          <age>16</age>
          <address>beijing</address>
    </employee>
</employees>

F:/poem1.xml

<?xml version="1.0" encoding="GB2312"?>
<employees>
    <employee id="001" gender = "male">
          <name>susan</name>
          <age>32</age>
          <address>8888888888</address>
    </employee>
    <employee gender = "female">
          <name>tom</name>
          <age>16</age>
          <address> beijing </address>
    </employee>
</employees>

(3)运行结果如下所示:

输出到控制台的XML文档:
<?xml version="1.0" encoding="GB2312" standalone="no"?>
<employees>
<employee gender="male" id="001">
<name>susan</name>
<age>32</age>
<address>8888888888</address>
</employee>
<employee gender="female" id="">
<name>tom</name>
<age>16</age>
<address> beijing </address>
</employee>
</employees>
文件(包括路径和后缀):F:\poem1.xml

源程序解读

(1)readXMLFile()方法创建DOM解析器的工厂实例,工厂实例的newDocumentBuilder()方法获得新的文档解析器。将传入的文件根据parse()方法解析获得Document对象。其中Document的normalize()方法可以去掉XML文档中作为格式化内容的空白处。根据getDocumentElement()方法获得XML文档的根节点employee。再根据getElements-ByTagName()方法获得节点employee的节点列表,循环节点列表获得每个employee节点的具体信息。根据getElementsByTagName()方法获得节点的子节点,根据getAttribute()方法获得节点的属性,并将获得的employee相应的信息设置保存到列表中。

(2)writeXMLFile()方法创建DOM解析器的工厂实例,创建新的DOM解析器,并创建新的Document文档。根据createElement()方法在文档中创建根节点。循环遍历员工列表,根据createElement()方法创建节点,setAttribute()方法是创建相应节点的属性,appendChild()方法是将子节点添加到其父节点中或为节点添加内容,createTextNode()方法是创建一个文本来为节点赋内容。

(3)domDocToFile()方法获得XSL引擎对象工厂实例创建XSL引擎。运用XSL引擎对象获得XML文档的转换器对象,转换器对象根据getOutputProperties()方法获得属性对象,获得的属性对象设置文档属性的编码格式以及输出格式。根据结果流对象设置文档的输出格式,包括将文档输出到控制台和将文档输出到指定路径的文件中。运用转换器的transform()方法将文档源结合输出格式对文档进行输出处理。

实例108 SAX解析XML文件

SAX(Simple API for XML)是基本事件的方法,很类似于标签库的处理机制,在标签开始、结束以及错误发生等地方调用相应的接口实现方法,不是全部文档都读入内存。SAX具有优异的性能和利用更少的存储空间特点。需要到官方网站http://xml.apache.org/dist/xerces-j/下载开发用的jar文件。文件下载解压缩后,将相应的xercesImpl.jar包放入项目的类库中。本实例介绍如何运用多种方式构造SAX解析器以及扩展相应的类实现解析XML文件。

技术要点

运用SAX解析XML文件的技术要点如下:

• SAX解析XML文档必须扩展ContentHandler、ErrorHandler、DTDHandler等,但是必须扩展ContentHandler(或者DefaultHandler)。

• SAX采用基于事件驱动的处理模式,它将XML文档转化成一系列的事件,由单独的事件处理器来决定如何处理。基于事件的处理模式主要是围绕着事件源以及事件处理器(或者叫监听器)来工作的。一个可以产生事件的对象被称为事件源,而可以针对事件产生响应的对象就被叫做事件处理器。事件源和事件处理器是通过在事件源中的事件处理器注册方法连接的。这样当事件源产生事件后,调用事件处理器相应的处理方法,一个事件就获得了处理。当然在事件源调用事件处理器中特定方法的时候,会传递给事件处理器相应事件的状态信息,这样事件处理器才能够根据事件信息来决定自己的行为。

• 在SAX接口中,事件源是org.xml.sax包中的XMLReader,它通过parse()方法来开始解析XML文档并根据文档内容产生事件。而事件处理器则是org.xml.sax包中的ContentHandler、DTDHandler、ErrorHandler以及EntityResolver这四个接口。它们分别处理事件源在解析过程中产生的不同种类的事件(其中DTDHandler是为解析文档DTD时而用)。而事件源XMLReader和这四个事件处理器的连接是通过在XMLReader中的相应的事件处理器注册方法set***()来完成的。

实现步骤

(1)创建一个类名为TextSAXParseXML.java。

(2)代码如下所示:

package com.zf.s12;                                    //创建一个包
import java.io.File;                                   //引入类
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.XMLFilter;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLFilterImpl;
import org.xml.sax.helpers.XMLReaderFactory;
class SAXFilter extends XMLFilterImpl {               //扩展相应过滤器类实现操作XML文档
    private String currentElement;                    //当前元素
    public SAXFilter(XMLReader parent) {              //带参数构造方法
          super(parent);                              //继承父类
    }
    public void startElement(String url, String elementName, String fullName,
              Attributes attributes) throws SAXException {//重写,过滤掉元素<flower>的开始事件
          currentElement = elementName;
          if (!elementName.equals("flower")) {         //判断节点
                                                       //调用父方法
              super.startElement(url, elementName, fullName, attributes);
          }
    }
    public void endElement(String namespaceURI, String localName,
              String fullName) throws SAXException {   //过滤掉元素<flower>的结束事件
        if (!localName.equals("flower")) {             //判断节点
              super.endElement(namespaceURI,
                            localName, fullName);      //调用父方法
        }
    }
    public void characters(char[] buffer, int start, int length)
              throws SAXException {                    //过滤掉元素<flower>中的内容
        if (!currentElement.equals("flower")) {        //判断节点
              super.characters(buffer, start, length); //调用父方法
        }
    }
}
class SAXHandler extends DefaultHandler {             //扩展相应类使用SAX读取XML文件
    public void startDocument() throws SAXException//重写方法,用于处理文档解析开始事件
    {                                                 //输出XML文档头部
        System.out.println("<?xml version=\"1.0\" encoding='gb2312'?>");
    }
    public void processingInstruction(String target, String data)
              throws SAXException {//重写方法处理解析中产生处理指令事件
        System.out.println("<?" + target + " " + data + "?>");
    }
    public void startElement(String uri, String localName, String qName,
              Attributes attrs) throws SAXException { //重写的方法
        //处理元素开始事件从参数中可以获得元素所在名称空间uri、元素名称、属性列表等信息
        System.out.print("<" + qName);
        int len = attrs.getLength();
        for (int i = 0; i < len; i++) {
              System.out.print(" ");
              System.out.print(attrs.getQName(i));
              System.out.print("=\"");
              System.out.print(attrs.getValue(i));
              System.out.print("\"");
        }
        System.out.print(">");
    }
    //重写的方法处理元素的字符内容,从参数中可以获得内容
    public void characters(char[] ch, int start, int length)
              throws SAXException {
        System.out.print(new String(ch, start, length));
    }
    //重写的方法处理元素结束事件,从参数中可以获得元素所在名称空间的uri、元素名称等信息
    public void endElement(String uri, String localName, String qName)
              throws SAXException {
        System.out.print("</" + qName + ">");
    }
}
public class TextSAXParseXML {                        //操作使用SAX解析XML文档的类
                                                      //第一种构造解析器的方法
    public static void oneCreateSAXParser(String fileName) {
        SAXParserFactory spf = SAXParserFactory
                  .newInstance();                     //创建SAX解析器工厂实例
        try {
              SAXParser sp = spf.newSAXParser();      //创建SAX解析器
              sp.parse(new File(fileName), new SAXHandler());//开始解析文档
        } catch (Exception e) {                       //捕获异常
              e.printStackTrace();
        }
    }
                                                      //第二种构造解析器的方法
    public static void twoCreateSAXParser(String fileName)throws Exception {
        XMLReader reader = XMLReaderFactory           //根据XML读工厂类创建XML读取对象
                  .createXMLReader("org.apache.xerces.parsers.SAXParser");
                                                      //创建ContentHandler的实例
        ContentHandler contentHandler = new SAXHandler();
        reader.setContentHandler(contentHandler);//reader中注册实例化contentHandler
        reader.parse(fileName);                       //开始解析文档
    }
                                                      //第三种构造解析器的方法
    public static void threeCreateSAXParser(String fileName) throws Exception {
        XMLReader reader = XMLReaderFactory//根据XML读取工厂类创建XML读取对象
                  .createXMLReader("org.apache.xerces.parsers.SAXParser");
        XMLFilter filter = new SAXFilter(reader);     //初始化过滤器
                                                  //过滤后的事件流设置DefaultHandler
        DefaultHandler defaultHandler = new SAXHandler();
        filter.setContentHandler(defaultHandler);//为过滤后的事件流设置defaultHandler
        filter.setErrorHandler(defaultHandler);//为过滤后的事件流设置ErrorHandler
        filter.parse(fileName);                      //开始解析文档
    }
    public static void main(String args[]) {         //Java程序主入口处
        String fileName = "F:/test.xml";             //文件
        try {
              System.out.println("1.运用扩展XMLFilterImpl过滤类实现SAX解析XML文档");
              threeCreateSAXParser(fileName);
              System.out.println();
              System.out.println("2.运用扩展DefaultHandler类实现SAX解析XML文档");
              oneCreateSAXParser(fileName);
              System.out.println();
              System.out.println("3.运用创建ContentHandler类实现SAX解析XML文档");
              twoCreateSAXParser(fileName);
        } catch (Exception e) {                      //捕获异常
              System.out.println("SAX解析XML文档出错:" + e.getMessage());
              System.exit(1);                        //退出0表示正常退出,1表示非正常
        }
    }
}

(3)运行结果如下所示:

1.运用扩展XMLFilterImpl过滤类实现SAX解析XML文档
<?xml version="1.0" encoding='gb2312'?>
<trees>
  <tree id="001" name="rongshu">
    <description>It is a big tree</description>
  </tree>
  <tree id="003" name="songshu">
    <description>The tree is very beautiful</description>
  </tree>
  <date type="calendar">2009-04-10</date>
</trees>
2.运用扩展DefaultHandler类实现SAX解析XML文档
<?xml version="1.0" encoding='gb2312'?>
<trees>
  <tree id="001" name="rongshu">
    <description>It is a big tree</description>
  </tree>
  <tree id="003" name="songshu">
    <description>The tree is very beautiful</description>
......

源程序解读

(1)SAXFilter类中的startElement()方法是重写过滤类的方法,判断传入的节点调用父类的该方法过滤掉开始节点的事件。endElement()方法是重写过滤类的方法,根据传入的指定节点调用父类的该方法过滤掉结束节点的事件。characters()方法判断传入的节点,调用父类的该方法删除节点的内容。

(2)SAXHandler类中的startDocument()方法是设置XML文档的版本和编码格式。

processingInstruction()方法是处理解析中产生的处理指令事件。startElement()方法循环遍历属性列表信息。getQName()方法获得属性的名称,getValue()方法获得属性的值。endElement()方法输出文档中节点的结束语句。characters()方法处理节点的字符内容。

(3)oneCreateSAXParser()方法创建SAX解析器工厂实例,运用解析器工厂的方法获得SAX解析器,调用parse()方法开始解析文档。

(4)twoCreateSAXParser()方法根据引入类创建XML读取对象,设置XML读取对象的ContentHandler,并调用parse()方法开始解析文档。

(5)threeCreateSAXParser()方法根据引入类创建XML读取对象。初始化XML过滤器对象并设置过滤器的ContentHandler和ErrorHandler,调用parse()方法对文档进行解析。

实例109 W3C解析XML文件

W3C(World Wide Web Consortium)中文意思是W3C理事会或万维网联盟,W3C以开发“We b事实标准”的各种技术规范作为其核心任务,已开发数多个技术规范。本实例介绍如何使用W3C技术将植物信息保存到XML文档中并从XML文档中读取信息,以及介绍使用W3C的规范。

技术要点

运用W3C解析XML文件的技术要点如下:

• W3C已开发了超过50个规范(草案)。这些规范(草案)包括人们早已耳熟能详的HTML、HTTP、URLs、XML等,也包括针对语义Web的RDF、OWL等。

• HTML/XHTML:HTML是Web的基础之一,基于HTML,Web上开始出现丰富多彩的页面,蕴涵了各种信息。基于HTML,Web以一种简便易用的方式走出了象牙塔,成为全社会的公共资源和财富。W3C先后推出了多个HTML版本。

• XML 1.0是W3C最具前瞻性和最有影响的标准之一。XML作为下一代Web的第一块重要基石,为分布式的、异构的数据交换提供了强大的功能,并且将数据本身和数据的表现分离,同时,就数据本身而言,数据的值和语义也是适当分离的。

实现步骤

(1)创建一个类名为TextW3CParseXML.java。

(2)代码如下所示:

package com.zf.s12;                              //创建一个包
import java.io.FileOutputStream;                 //引入类
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Iterator;
import java.util.Vector;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
class Plant {                                    //植物类
    private String id;                           //植物编号
    private String name;                         //植物名称
    private String desc;                         //植物描述
    public String getDesc() {
          return desc;
    }
    public void setDesc(String desc) {
          this.desc = desc;
    }
    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 class TextW3CParseXML {                        //操作使用W3C解析XML文档的类
    Vector plant_Vector;
    private Vector readXMLFile(String file) throws Exception {
        DocumentBuilderFactory dbf = DocumentBuilderFactory
                                  .newInstance();     //创建解析器工厂
        DocumentBuilder builder = dbf.newDocumentBuilder();//创建文档解析对象
        Document doc = builder.parse(file);           //解析文件获得文档
        Element root = doc.getDocumentElement();      //获取根元素
        NodeList plantList = root.getElementsByTagName("plant");
        plant_Vector = new Vector();
        for (int i = 0; i < plantList.getLength(); i++) {   //循环遍历植物列表
                                                                  //一次取得每一个植物元素
              Element element = (Element) plantList.item(i);
                                                                  //创建一个植物的实例
              Plant plant = new Plant();
              plant.setId(element.getAttribute("id")); //设置植物编号
              NodeList names = element.getElementsByTagName("name");//获得名称节点列表
              Element e = (Element) names.item(0);     //获得第一个植物名称的元素
              Node t = e.getFirstChild();              //获得第一个节点
              plant.setName(t.getNodeValue());         //设置植物名称
              NodeList desc = element.getElementsByTagName("description");
              e = (Element) desc.item(0);
              t = e.getFirstChild();
              plant.setDesc(t.getNodeValue());         //设置植物描述
              plant_Vector.add(plant);                 //将植物对象添加到集合中
        }
        return plant_Vector;
    }
    public static void callWriteXmlFile(Document doc,
              Writer w, String encoding) {             //文档写入XML文件中
        try {
              Source source = new DOMSource(doc);
              Result result = new StreamResult(w);     //创建结果对象
              Transformer xformer = TransformerFactory.newInstance()
                        .newTransformer();             //创建转换器
                                                                  //设置编码格式
              xformer.setOutputProperty(OutputKeys.ENCODING, encoding);
              xformer.transform(source, result);       //对文档进行转换
        } catch (Exception e) {                        //捕获异常
              e.printStackTrace();
        }
    }
    private void writeXMLFile(String fileName) {       //将XML文件复制到指定文件
        DocumentBuilderFactory dbf = DocumentBuilderFactory
                      .newInstance();                  //创建解析器工厂
        DocumentBuilder builder = null;
        try {
            builder = dbf.newDocumentBuilder();        //创建文档解析器
        } catch (Exception e) {
        }
        Document doc = builder.newDocument();          //创建新文档
        Element root = doc.createElement("plants");
        doc.appendChild(root);                         //将根元素添加到文档上
        for (int i = 0; i < plant_Vector.size(); i++) {     //获取植物信息
            Plant plant = (Plant) plant_Vector.get(i);      //创建一个植物
            Element el = doc.createElement("plant");
            el.setAttribute("id", plant.getId());
            root.appendChild(el);                      //添加属性
            Element name = doc.createElement("name");  //创建文本名称节点
            el.appendChild(name);
            Text tname = doc.createTextNode(plant.getName());
            name.appendChild(tname);
            Element desc = doc.createElement("description");//创建文本描述节点
            el.appendChild(desc);                      //将desc添加到学生节点上
            Text tage = doc.createTextNode(String.valueOf(plant.getDesc()));
            desc.appendChild(tage);                    //将文本节点放在desc节点上
        }
        try {
            FileOutputStream fos = new FileOutputStream(fileName);//创建文件输出流
                                                  //创建输出流写对象
            OutputStreamWriter outwriter = new OutputStreamWriter(fos);
            callWriteXmlFile(doc, outwriter, "gb2312");//调用方法将数据写入XML文档中
            outwriter.close();                         //释放资源
            fos.close();
        } catch (Exception e) {                        //捕获异常
            e.printStackTrace();
        }
    }
    public static void main(String args[]) {          //Java程序主入口处
        String fileName = "F:/plant.xml";             //文件
        TextW3CParseXML t = new TextW3CParseXML();    //实例化对象
        try {
            Vector v = t.readXMLFile(fileName);       //调用方法获得Vector向量集合
            Iterator it = v.iterator();               //获得游标集合
            while (it.hasNext()) {                    //循环输出XML文档中的信息
                  Plant s = (Plant) it.next();
                  System.out.println(s.getId() + "\t" + s.getName() + "\t"
                            + s.getDesc());
            }
        } catch (Exception e) {                        //捕获异常
            System.out.println("读取XML文档出错:"+e.getMessage());
        }
        String outfile = "F:/plant1.xml";              //文件
        t.writeXMLFile(outfile);                       //调用方法将XML文件复制指定文件
    }
}

(3)运行结果如下所示:

001 rose     Rose is very beautiful but there are many thorns
002 lily     I love Lily
003 cactus   A cactus that radiates spines

源程序解读

(1)readXMLFile()方法首先创建解析器工厂,再调用工厂方法创建文档解析对象,根据parse()方法解析文件生成Document文档对象。依据Document的getDocumentElement()方法获得文档根节点(目录),依据getElementsByTagName()方法获得plant节点列表,循环节点列表获得每个节点的信息。getAttribute()方法获得指定节点的属性,getElementsByTagName()方法获得指定节点的子节点列表,item()方法获得节点下面第一个节点列表的信息。getFirstChild()方法获得节点列表的第一个节点。getNodeValue()获得节点的内容。循环获得的数据按要求放入向量集合中。

(2)callWriteXmlFile()方法创建文档源对象和结果对象,并设置写入XML文件中的数据的编码格式,运用transform()方法对文档进行转换,将文档写入文件中。

(3)writeXMLFile()方法创建解析器工厂,调用工厂方法创建新的文档解析对象,根据newDocument()方法获得新的文档对象。createElement()方法创建根节点,appendChild()方法将根节点添加到文档对象中。循环向量集合获得每个植物的具体信息,将每个植物的信息添加到文档对象中。创建文件输出流和输出流写对象,调用callWriteXmlFile()方法将文档对象中的内容写入到指定文件中。