Friday, April 6, 2007

用xalan在ie中浏览xml文件

用xalan在ie中浏览xml文件
文章出处:www.ccidnet.com 作者:�   发布时间:2004-12-28   点击:0
 

  xml(extensible markup language)自从出现以来,因为良好的表达数据格式能力和扩展性,被广泛地用于数据的表达、传输和行业标准的定义中,尤其常用于表示internet上的数据。在用xml表达数据时,常常需要把xml里的数据用一定的格式显示出来,例如通过ie中的表格方式进行显示。apache里面的xalan就是这样一个优秀的工具,它能够把xml里的数据转换为不同格式的文件,甚至能够将xml和样式表转换为html文件。xalan可以在 http://xml.apache.org/xalan-j网址获得。 

  下面我们从xml和它的样式表讲起,看看xalan是如何的强大和方便的。 

  xml文件和样式表

   xml文件需要用一定的格式表示出来,这就需要样式表,现在的样式表一般是一个.xsl文件。在样式表中能够使用html的元素,比如下面的xml文件: 

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="sample.xsl"?>
<info>
<person>
<name>aaa</name>
<age>24</age>
<addr>one</addr>
</person>
<person>
<name>bbb</name>
<age>25</age>
<addr>two</addr>
</person>
</info>


  这时如果我们加入样式表,ie就能够以不同的方式显示上面xml文件里面的内容。ie的工作方式如图1。 
           

  图1 ie工作方式图

<?xml version="1.0"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/xsl/transform " version="1.0">
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates select="info" />
</body>
</html>
</xsl:template>
<xsl:template match="info">
<table>
<th>name</th>
<th>age</th>
<th>address</th>
<xsl:apply-templates select="person" />
</table>
</xsl:template>
<xsl:template match="person">
<tr>
<td><xsl:value-of select="name" /></td>
<td><xsl:value-of select="age" /></td>
<td><xsl:value-of select="addr" /></td>
</tr>
</xsl:template>
</xsl:stylesheet>


  从上面代码中可以看到,这个xsl文件里面包含的是html的元素。其中<xsl:template match="/">表示解析到xml文件的根元素时生成的区段代码。xsl文件中生成的是html文件的头信息。<xsl:apply-templates select="info" />的意思是当解析xml文件<info>这个元素时,生成它下面描述的代码。xsl文件中生成的是html中的表格格式,包括表格头。<xsl:apply-templates select="person" />的意思是在解析xml文件中<person>这个元素时生成它描述的代码,这里生成的是html中表格的数据。<xsl:value-of select="name" />是将<name>这个元素描述的值取出来,得到的就是"aaa"和"bbb"。 

  编辑好xml文件和xsl文件后,将上面两个文件放在同一个目录下,用ie打开这个xml文件,结果如图2。 
                



  图2 ie打开xml文件显示图

  使用程序实现xml+xsl=html

  有时候,我们需要在程序中做xml+xsl=html这种转换。这时候就可以用apache提供的xalan。xalan是一个非常优秀的、用于xml转换的api,使用来很方便。同时它还提供了现成的程序,直接使用它附带的程序也可以进行这种转换。 

  xalan中有很多类,其中包括xml的解析类,可以用这个类做解析xml的工作。xalan最核心的类还是javax. // 将xalan里所用的包import进来

import javax.xml.transform.transformerfactory;
import javax.xml.transform.transformer;
import javax.xml.transform.stream.streamsource;
import javax.xml.transform.stream.streamresult;
import javax.xml.transform.transformerexception;
import javax.xml.transform.transformer configurationexception;
// 将需要的java类import进来
import java.io.fileoutputstream;
import java.io.filenotfoundexception;
import java.io.ioexception;
public class simpletransform
{
public static void main(string[] args)
throws transformerexception, transformerconfigurationexception, 
filenotfoundexception, ioexception
{ 
//创建一个类transformer的类厂
transformerfactory tfactory = transformerfactory.newinstance();
//用该类厂创建一个transformer的实例
transformer transformer = tfactory.newtransformer(new 
streamsource("sample.xsl"));
//调用transformer类的transform函数完成转换
transformer.transform(new streamsource("sample.xml"), new streamresult(new 
fileoutputstream("sample.html")));
//打印出信息,告诉用户结果在sample.html文件中
system.out.println("************* the result is in sample.html 
*************");
}
}


  编译并且运行该程序,就可以在相同的目录下看见sample.html文件。用ie打开该文件,和上面用ie打开sample.xml文件的结果是一样的。有时在网络上发布信息时,可以提供给用户不同的视图。我们事先做好一些不同的xsl模板,然后根据用户的需要将xml和不同的xsl生成的html返回给用户,这样就可以在服务器端将数据和视图分开,根据用户的不同请求返回不同的视图了。 


  web方式的使用方法
  图3是以web方式使用xalan的常用方法。  
           





  图3 web方式使用xalan的方法图


  在web浏览的时候,用户通过浏览器向服务器提交自己的http请求,然后由服务器的jsp程序解析用户的请求,根据用户的请求生成相应的html文件后返回给浏览器。 

简析java的xml编程

简析java的xml编程
文章出处:www.ccident.cn 作者:徐荣胜      发布时间:2004-12-31   点击:0
 

  xml作为全球通用的结构化语言,越来越受人们青睐,各种开发平台(比如microsoft studio系列、oracle系列、inprise borland系列等)也都把支持xml开发作为宣传口号之一 。由于笔者所从事的电子政务开发较早的引入了xml,所以尝到了许多甜头,在许多项目中利用xml数据交换信息,省去了许多麻烦事,不用制定繁锁的数据格式,利用xml数据易于表达,也利于一线开发者跟踪调试。 

  笔者先前也曾发表过相关的文章,比如《简析delphi中的xml编程》一文,有兴趣的读者可以到google网( http://www.google.com)去搜索一下,有很多媒体转载。今天笔者想探讨的是关于java中的xml编程,希望对正在或想要学习xml编程的新老读者有所帮助 。 

  在xml应用中,最常用也最实用的莫过于xml文件的读写,所以笔者通过一个简单的xml文件读写来作简要分析。可以在任何文本编辑器中先建立如下结构的xml文件,类似于html结构,但xml语义比较严格,起始标记必须配对,比如"〈学生花名册〉"与"〈/学生花名册〉"对应,空格多少可不必在意,但一般都以缩格形式书写,便于阅读。把此文件命名为input.xml, 可以在任何支持xml的浏览器中打开测试一下,如果输入正确,在浏览中可以看到此文件的树形表示结构。如果您还对xml结构感到比较陌生,建议先看看《简析delphi中的xml编程》一文中关于xml文件的说明。 

<?xml version="1.0" encoding="gb2312"?> 
<学生花名册> 
<学生 性别 = "男"> 
<姓名>李华</姓名> 
<年龄>14</年龄> 
<电话>6287555</电话> 
</学生> 
<学生 性别 = "男"> 
<姓名>张三</姓名> 
<年龄>16</年龄> 
<电话>8273425</电话> 
</学生> 
</学生花名册>


  准备工作做完后,接着就开始写实质性的java代码了。为保存从xml文件读入的信息,需要先建一个简单的bean来保存学生信息,命名为studentbean,代码如下所示: 

public class studentbean { 
private string sex; //学生性别 
private string name; //学生姓名 
private int age; //学生年龄 
private string phone; //电话号码 

public void setsex(string s) { 
sex = s; 
} 
public void setname(string s) { 
name = s; 
} 
public void setage(int a) { 
age = a; 
} 
public void setphone(string s) { 
phone = s; 
} 
public string getsex() { 
return sex; 
} 
public string getname() { 
return name; 
} 
public int getage() { 
return age; 
} 
public string getphone() { 
return phone; 
} 
}

   之后写xml的测试类,笔者把这个类命名为xmltest,为了读写xml文件,需要导入如下java包,"//"后为注释说明,笔者的环境是jdk 1.3.1_04,在jdk 1.4.0中测试也通过,xml解释器用apache的crimson,可以到apache主页去上载。 

import java.io.*; //java基础包,包含各种io操作 
import java.util.*; //java基础包,包含各种标准数据结构操作 
import javax.xml.parsers.*; //xml解析器接口 
import org.w3c.dom.*; //xml的dom实现 
import org.apache.crimson.tree.xmldocument;//写xml文件要用到


  为了保存多个学生信息,还得借助一个集合类(并不是单纯意义上的集合,java中的集合是集合框架的概念,包含向量、列表、哈希表等),这里采用vector向量类。定义在xmltest测试类中,命名为student_vector。然后定义两个方法readxmlfile和writexmlfile,实现读写操作。代码如下: 

private void readxmlfile(string infile) throws exception { 
//为解析xml作准备,创建documentbuilderfactory实例,指定documentbuilder 
documentbuilderfactory dbf = documentbuilderfactory.newinstance(); 
documentbuilder db = null; 
try { 
db = dbf.newdocumentbuilder(); 
} catch (parserconfigurationexception pce) { 
system.err.println(pce); //出异常时输出异常信息,然后退出,下同 
system.exit(1); 
} 

document doc = null; 
try { 
doc = db.parse(infile); 
} catch (domexception dom) { 
system.err.println(dom.getmessage()); 
system.exit(1); 
} catch (ioexception ioe) { 
system.err.println(ioe); 
system.exit(1); 
}



//下面是解析xml的全过程,比较简单,先取根元素"学生花名册" 

element root = doc.getdocumentelement(); 
//取"学生"元素列表 
nodelist students = root.getelementsbytagname("学生"); 
for (int i = 0; i < students.getlength(); i++) { 
//依次取每个"学生"元素 
element student = (element) students.item(i); 
//创建一个学生的bean实例 
studentbean studentbean = new studentbean(); 
//取学生的性别属性 
studentbean.setsex(student.getattribute("性别")); 
//取"姓名"元素,下面类同 
nodelist names = student.getelementsbytagname("姓名"); 
if (names.getlength() == 1) { 
element e = (element) names.item(0); 
text t = (text) e.getfirstchild(); 
studentbean.setname(t.getnodevalue()); 
} 

nodelist ages = student.getelementsbytagname("年龄"); 
if (ages.getlength() == 1) { 
element e = (element) ages.item(0); 
text t = (text) e.getfirstchild(); 
studentbean.setage(integer.parseint(t.getnodevalue())); 
} 

nodelist phones = student.getelementsbytagname("电话"); 
if (phones.getlength() == 1) { 
element e = (element) phones.item(0); 
text t = (text) e.getfirstchild(); 
studentbean.setphone(t.getnodevalue()); 
} 

student_vector.add(studentbean); 
} 
} 

private void writexmlfile(string outfile) throws exception { 
//为解析xml作准备,创建documentbuilderfactory实例,指定documentbuilder 
documentbuilderfactory dbf = documentbuilderfactory.newinstance(); 
documentbuilder db = null; 
try { 
db = dbf.newdocumentbuilder(); 
} catch (parserconfigurationexception pce) { 
system.err.println(pce); 
system.exit(1); 
} 

document doc = null; 
doc = db.newdocument(); 

//下面是建立xml文档内容的过程,先建立根元素"学生花名册" 
element root = doc.createelement("学生花名册"); 
//根元素添加上文档 
doc.appendchild(root); 

//取学生信息的bean列表 
for (int i = 0; i < student_vector.size(); i++) { 
//依次取每个学生的信息 
studentbean studentbean = (studentbean) student_vector.get(i); 
//建立"学生"元素,添加到根元素 
element student = doc.createelement("学生"); 
student.setattribute("性别", studentbean.getsex()); 
root.appendchild(student); 
//建立"姓名"元素,添加到学生下面,下同 
element name = doc.createelement("姓名"); 
student.appendchild(name); 
text tname = doc.createtextnode(studentbean.getname()); 
name.appendchild(tname); 

element age = doc.createelement("年龄"); 
student.appendchild(age); 
text tage = doc.createtextnode(string.valueof(studentbean.getage())); 

age.appendchild(tage); 

element phone = doc.createelement("电话"); 
student.appendchild(phone); 
text tphone = doc.createtextnode(studentbean.getphone()); 
phone.appendchild(tphone); 
} 
//把xml文档输出到指定的文件 
fileoutputstream outstream = new fileoutputstream(outfile); 
outputstreamwriter outwriter = new outputstreamwriter(outstream); 
((xmldocument) doc).write(outwriter, "gb2312"); 
outwriter.close(); 
outstream.close(); 
}


  最后加入测试主函数,如下: 

public static void main(string[] args) throws exception { 
//建立测试实例 
xmltest xmltest = new xmltest(); 
//初始化向量列表 
xmltest.student_vector = new vector(); 

system.out.println("开始读input.xml文件"); 
xmltest.readxmlfile("input.xml"); 

system.out.println("读入完毕,开始写output.xml文件"); 
xmltest.writexmlfile("output.xml"); 
system.out.println("写入完成"); 
}


  好了,保存好studentbean和xmltest,把input.xml保存到工作目录下。如果您输入很仔细,没敲错字母的话,可以看到"写入完成"了,去瞧瞧output.xml文件和input.xml文件是不是一样吧。
记住我的e-mail:nbdeveloper@hotmail.com。 

Java解析XML文档_dom解析xml

Linux宝库 - Java解析XML文档――dom解析xml

  一、前言
  
  用Java解析XML文档,最常用的有两种方法:使用基于事件的XML简单API(Simple API for XML)称为SAX和基于树和节点的文档对象模型(Document Object Module)称为DOM。Sun公司提供了Java API for XML Parsing(JAXP)接口来使用SAX和DOM,通过JAXP,我们可以使用任何与JAXP兼容的XML解析器。
  
  JAXP接口包含了三个包:
  
  (1)org.w3c.dom W3C推荐的用于XML标准规划文档对象模型的接口。
  
  (2)org.xml.sax  用于对XML进行语法分析的事件驱动的XML简单API(SAX)
  
  (3)javax.xml.parsers解析器工厂工具,程序员获得并配置特殊的特殊语法分析器。
  
  二、前提
  
  DOM编程不要其它的依赖包,因为JDK里自带的JDK里含有的上面提到的org.w3c.dom、org.xml.sax 和javax.xml.parsers包就可以满意条件了。
  
  三、使用DOM解析XML文档
  
  我们现在来看看DOM是如何解析XML的吧!同样的,我将从一个简单的不能再简单的例子来说明DOM是如何解析XML文档的,先让我们看看XML是什么内容吧:
  
  <?xml version="1.0" encoding="gb2312"?>
  
  <books>
  
  <book email="zhoujunhui">
  
  <name>rjzjh</name>
  
  <price>jjjjjj</price>
  
  </book>
  
  </books>
  
  简单的不能再简单了。但是该有的都有了,根元素、属性、子节点。好了,能反应问题就行了,下面来看看解析这个XML文件的Java代码吧!
  
  1 public class DomParse {
  
  2   public DomParse(){
  
  3      DocumentBuilderFactory domfac= DocumentBuilderFactory.newInstance();
  
  4      try {
  
  5          DocumentBuilder dombuilder=domfac.newDocumentBuilder();
  
  6          InputStream is=new FileInputStream("bin/library.xml");
  
  7          Document doc=dombuilder.parse(is);
  
  8
  
  9          Element root=doc.getDocumentElement();
  
  10         NodeList books=root.getChildNodes();
  
  11         if(books!=null){
  
  12             for(int i=0;i<books.getLength();i++){
  
  13                Node book=books.item(i);
  
  14                if(book.getNodeType()==Node.ELEMENT_NODE){
  
  15         String email= book.getAttributes().getNamedItem("email").getNodeValue();
  
  16                    System.out.println(email);
  
  17         for(Node node=book.getFirstChild();node!=null;node=node.getNextSibling ()){
  
  18                       if(node.getNodeType()==Node.ELEMENT_NODE){
  
  19                           if(node.getNodeName().equals("name")){
  
  20                              String name= node.getNodeValue();
  
  21                              String name1=node.getFirstChild().getNodeValue();
  
  22                              System.out.println(name);
  
  23                               System.out.println(name1);
  
  24                           }
  
  25                           if(node.getNodeName().equals("price")){
  
  26                              String price=node.getFirstChild ().getNodeValue();
  
  27                              System.out.println(price);
  
  28                           }
  
  29                       }
  
  30                    }
  
  31                }
  
  32             }
  
  33         }
  
  34      } catch (ParserConfigurationException e) {
  
  35         e.printStackTrace();
  
  36      } catch (FileNotFoundException e) {
  
  37         e.printStackTrace();
  
  38      } catch (SAXException e) {
  
  39         e.printStackTrace();
  
  40      } catch (IOException e) {
  
  41         e.printStackTrace();
  
  42      }
  
  43  }
  
  44  public static void main(String[] args) {
  
  45      new DomParse();
  
  46  }
  
  47 }
  
  四、代码解释
  
  先看看这个程序引用类:
  
  import java.io.FileInputStream ;
  
  import java.io.FileNotFoundException;
  
  import java.io.IOException;
  
  import java.io.InputStream;
  
  import javax.xml.parsers.DocumentBuilder;
  
  import javax.xml.parsers.DocumentBuilderFactory ;
  
  import javax.xml.parsers.ParserConfigurationException;
  
  //下面主要是org.xml.sax包的类
  
  import org.w3c.dom.Document;
  
  import org.w3c.dom.Element;
  
  import org.w3c.dom.Node;
  
  import org.w3c.dom.NodeList;
  
  import org.xml.sax.SAXException;
  
  上面那么简单的代码一看就明白了,但是为了介绍个DOM编程的大概还是来看看这个程序吧:
  
  (1)得到DOM解析器的工厂实例
  
  DocumentBuilderFactory domfac=DocumentBuilderFactory.newInstance ();
  
  得到javax.xml.parsers.DocumentBuilderFactory;类的实例就是我们要的解析器工厂
  
  (2)从DOM工厂获得DOM解析器
  
  DocumentBuilder dombuilder=domfac.newDocumentBuilder();
  
  通过javax.xml.parsers.DocumentBuilderFactory实例的静态方法newDocumentBuilder ()得到DOM解析器
  
  (3)把要解析的XML文档转化为输入流,以便DOM解析器解析它
  
  InputStream is=new FileInputStream("bin/library.xml");
  
  InputStream是一个接口。
  
  (4)解析XML文档的输入流,得到一个Document
  
  Document doc= dombuilder.parse(is);
  
  由XML文档的输入流得到一个org.w3c.dom.Document对象,以后的处理都是对Document对象进行的
  
  (5)得到XML文档的根节点
  
  Element root=doc.getDocumentElement();
  
  在DOM中只有根节点是一个org.w3c.dom.Element对象。
  
  (6)得到节点的子节点
  
  NodeList books=root.getChildNodes();
  
  for(int i=0;i<books.getLength();i++){
  
  Node book=books.item(i);
  
  }
  
  这是用一个org.w3c.dom.NodeList接口来存放它所有子节点的,还有一种轮循子节点的方法,后面有介绍
  
  (7)取得节点的属性值
  
  String email=book.getAttributes().getNamedItem("email").getNodeValue();
  
  System.out.println(email);
  
  注意,节点的属性也是它的子节点。它的节点类型也是Node.ELEMENT_NODE
  
  (8)轮循子节点
  
  for(Node node=book.getFirstChild();node!=null;node=node.getNextSibling()){
  
  if(node.getNodeType()==Node.ELEMENT_NODE){
  
  if(node.getNodeName().equals("name")){
  String name=node.getNodeValue ();
  
  String name1=node.getFirstChild().getNodeValue();
  
  System.out.println(name);
  
  System.out.println(name1);
  
  }
  
  if(node.getNodeName().equals("price")){
  
  String price=node.getFirstChild().getNodeValue();
  
  System.out.println(price);
  }
  
  }
  
  这段代码的打印输出为:
  
  null
  
  alterrjzjh
  
  jjjjjj
  
  从上面可以看出
  
  String name= node.getNodeValue();  是一个空值。而
  
  String name1=node.getFirstChild().getNodeValue(); 才是真正的值,这是因为DOM把<name>rjzjh</name>也当作是两层结构的节点,其父节点

java与xml联合编程之sax篇

java与xml联合编程之sax篇  
文章出处:www.ccidnet.com 作者:扬帆    发布时间:2004-12-28   点击:0
 

   sax概念 

  sax是simple api for xml的缩写,它并不是由w3c官方所提出的标准,可以说是"民间"的事实标准。实际上,它是一种社区性质的讨论产物。虽然如此,在xml中对sax的应用丝毫不比dom少,几乎所有的xml解析器都会支持它。 

  与dom比较而言,sax是一种轻量型的方法。我们知道,在处理dom的时候,我们需要读入整个的xml文档,然后在内存中创建dom树,生成dom树上的每个node对象。当文档比较小的时候,这不会造成什么问题,但是一旦文档大起来,处理dom就会变得相当费时费力。特别是其对于内存的需求,也将是成倍的增长,以至于在某些应用中使用dom是一件很不划算的事(比如在applet中)。这时候,一个较好的替代解决方法就是sax。 

  sax在概念上与dom完全不同。首先,不同于dom的文档驱动,它是事件驱动的,也就是说,它并不需要读入整个文档,而文档的读入过程也就是sax的解析过程。所谓事件驱动,是指一种基于回调(callback)机制的程序运行方法。(如果你对java新的代理事件模型比较清楚的话,就会很容易理解这种机制了) 
               




  在xmlreader接受xml文档,在读入xml文档的过程中就进行解析,也就是说读入文档的过程和解析的过程是同时进行的,这和dom区别很大。解析开始之前,需要向xmlreader注册一个contenthandler,也就是相当于一个事件监听器,在contenthandler中定义了很多方法,比如startdocument(),它定制了当在解析过程中,遇到文档开始时应该处理的事情。当xmlreader读到合适的内容,就会抛出相应的事件,并把这个事件的处理权代理给contenthandler,调用其相应的方法进行响应。 

  这样泛泛的说来或许有些不容易理解,别急,后面的例子会让你明白sax的解析过程。看看这个简单xml文件: 

ogden nash

adam


  当xmlreader读到<poem>标签时,就会调用contenthandler.startelement()方法,并把标签名poem作为参数传递过去。在你实现的startelement()方法中需要做相应的动作,以处理当<poem>出现时应该做的事情。各个事件随着解析的过程(也就是文档读入的过程)一个个顺序的被抛出,相应的方法也会被顺序的调用,最后,当解析完成,方法都被调用后,对文档的处理也就完成了。下面的这个表,列出了在解析上面的那个xml文件的时候,顺序被调用的方法: 

遇到的项目 方法回调 
{文档开始} startdocument() 
<poem> startelement(null,"poem",null,{attributes}) 
"\n" characters("\n...", 6, 1) 
<author>  startelement(null,"author",null,{attributes}) 
"ogden nash"  characters("\n...", 15, 10) 
</author>  endelement(null,"author",null) 
"\n" characters("\n...", 34, 1) 
<title>  startelement(null,"title",null,{attributes}) 
"fleas" characters("\n...", 42, 5) 
</title>  endelement(null,"title",null) 
"\n"  characters("\n...", 55, 1) 
<line>  startelement(null,"line",null,{attributes}) 
"adam"  characters("\n...", 62, 4) 
</line>  endelement(null,"line",null) 
"\n" characters("\n...", 67, 1) 
</poem> endelement(null,"poem",null) 
{文档结束} enddocument() 

   contenthandler实际上是一个接口,当处理特定的xml文件的时候,就需要为其创建一个实现了contenthandler的类来处理特定的事件,可以说,这个实际上就是sax处理xml文件的核心。下面我们来看看定义在其中的一些方法: 

void characters(char[] ch, int start, int length): 


  这个方法用来处理在xml文件中读到字符串,它的参数是一个字符数组,以及读到的这个字符串在这个数组中的起始位置和长度,我们可以很容易的用string类的一个构造方法来获得这个字符串的string类:string charencontered=new string(ch,start,length)。 

void startdocument():


   当遇到文档的开头的时候,调用这个方法,可以在其中做一些预处理的工作。 

void enddocument(): 


  和上面的方法相对应,当文档结束的时候,调用这个方法,可以在其中做一些善后的工作。 

void startelement(java.lang.string namespaceuri, java.lang.string localname, java.lang.string qname, attributes atts) 


  当读到一个开始标签的时候,会触发这个方法。在sax1.0版本中并不支持名域,而在新的2.0版本中提供了对名域的支持,这儿参数中的namespaceuri就是名域,localname是标签名,qname是标签的修饰前缀,当没有使用名域的时候,这两个参数都未null。而atts是这个标签所包含的属性列表。通过atts,可以得到所有的属性名和相应的值。要注意的是sax中一个重要的特点就是它的流式处理,在遇到一个标签的时候,它并不会纪录下以前所碰到的标签,也就是说,在startelement()方法中,所有你所知道的信息,就是标签的名字和属性,至于标签的嵌套结构,上层标签的名字,是否有子元属等等其它与结构相关的信息,都是不得而知的,都需要你的程序来完成。这使得sax在编程处理上没有dom来得那么方便。 

void endelement(java.lang.string namespaceuri, java.lang.string localname, java.lang.string qname) 


  这个方法和上面的方法相对应,在遇到结束标签的时候,调用这个方法。 

  因为contenthandler是一个接口,在使用的时候可能会有些不方便,因而,sax中还为其制定了一个helper类:defaulthandler,它实现了这个接口,但是其所有的方法体都为空,在实现的时候,你只需要继承这个类,然后重载相应的方法即可。 

  ok,到这儿sax的基本知识已经差不多讲完了,下面我们来看看两个具体的例子,以更好的理解sax地用法。 

  sax编程实例 

  我们还是沿用讲dom的时候使用的那个文档例子,但首先,我们先看一个简单一些的应用,我们希望能够统计一下xml文件中各个标签出现的次数。这个例子很简单,但是足以阐述sax编程的基本思路了。 

  一开始当然还是import语句了: 

import org.xml.sax.helpers.defaulthandler;
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import java.util.*;
import java.io.*; 


  然后,我们创建一个继承于defaulthandler的类,具体的程序逻辑在这儿可以暂且放在一边,要注意的是程序的结构: 

public class saxcounter extends defaulthandler {
private hashtable tags; //这个hashtable用来记录tag出现的次数
// 处理文档前的工作
public void startdocument() throws saxexception {
tags = new hashtable();//初始化hashtable
}
//对每一个开始元属进行处理
public void startelement(string namespaceuri, string localname,
string rawname, attributes atts)
throws saxexception
{
string key = localname;
object value = tags.get(key);
if (value == null) {
// 如果是新碰到的标签,这在hastable中添加一条记录
tags.put(key, new integer(1));
} else {
// 如果以前碰到过,得到其计数值,并加1
int count = ((integer)value).intvalue();

count++;
tags.put(key, new integer(count));
}
}
//解析完成后的统计工作
public void enddocument() throws saxexception {
enumeration e = tags.keys();
while (e.hasmoreelements()) {
string tag = (string)e.nextelement();
int count = ((integer)tags.get(tag)).intvalue();
system.out.println("tag <" + tag + "> occurs " + count
+ " times");
}
}
//程序入口,用来完成解析工作
static public void main(string[] args) {
string filename = null;
boolean validation = false;

filename="links.xml";
saxparserfactory spf = saxparserfactory.newinstance();
xmlreader xmlreader = null;
saxparser saxparser=null;
try {
// 创建一个解析器saxparser对象
saxparser = spf.newsaxparser();
// 得到saxparser中封装的sax xmlreader
xmlreader = saxparser.getxmlreader();
} catch (exception ex) {
system.err.println(ex);
system.exit(1);
}
try {
//使用指定的contenthandler,解析给xml文件,这儿要注意的是,为了
//程序的简单起见,这儿将主程序和contenthandler放在了一起。实际上
//main方法中所作的所有事情,都与contenthandler无关。
xmlreader.parse(new file(filename),new saxcounter());
} catch (saxexception se) {

system.err.println(se.getmessage());
system.exit(1);
} catch (ioexception ioe) {
system.err.println(ioe);
system.exit(1);
}
}
} 


  我们来看看这段程序作了些什么,在main()方法中,主要做的就是创建解析器,然后解析文档。实际上,在这儿创建saxparser对象的时候,为了使程序代码于具体的解析器无关,使用了同dom中一样的设计技巧:通过一个saxparserfactory类来创建具体的saxparser对象,这样,当需要使用不同的解析器的时候,要改变的,只是一个环境变量的值,而程序的代码可以保持不变。这就是factorymethod模式的思想。在这儿不再具体讲了,如果还有不明白的,可以参看上面dom中的解释,原理是一样的。 

  不过在这儿还有一点点要注意的地方,就是saxparser类和xmlreader类之间的关系。你可能有些迷糊了吧,实际上saxparser是jaxp中对xmlreader的一个封装类,而xmlreader是定义在sax2.0种的一个用来解析文档的接口。你可以同样的调用saxparser或者xmlreader中的parser()方法来解析文档,效果是完全一样的。不过在saxparser中的parser()方法接受更多的参数,可以对不同的xml文档数据源进行解析,因而使用起来要比xmlreader要方便一些。 

  这个例子仅仅涉及了sax的一点皮毛,而下面的这个,可就要高级一些了。下面我们要实现的功能,在dom的例子中已经有实现了,就是从xml文档中读出内容并格式化输出,虽然程序逻辑看起来还是很简单,但是sax可不比dom哦,看着吧。 

  前面说过,当遇到一个开始标签的时候,在startelement()方法中,我们并不能够得到这个标签在xml文档中所处的位置。这在处理xml文档的时候是个大麻烦,因为在xml中标签的语义,有一部分是由其所处的位置所决定的。而且在一些需要验证文档结构的程序中,这更是一个问题。当然,没有解决不了的问题了,我们可以使用一个栈来实现对文档结构的纪录。 

  栈的特点是先进先出,我们现在的想法是,在startelemnt()方法中用push将这个标签的名字添加到栈中,在endelement()方法中在把它pop出来。我们知道对一个结构良好的xml而言,其嵌套结构是完备的,每一个开始标签总会对应一个结束标签,而且不会出现标签嵌套之间的错位。因而,每一次startelement()方法的调用,必然会对应一个endelement()方法的调用,这样push和pop也是成对出现的,我们只需要分析栈的结构,就可以很容易的知道当前标签所处在文档结构中的位置了。 

public class saxreader extends defaulthandler {
java.util.stack tags=new java.util.stack();
//--------------xml content-------------
string text=null;
string url=null;
string author=null;
string description=null;
string day=null;
string year=null;
string month=null;
//----------------------------------------------
public void enddocument() throws saxexception {
system.out.println("------parse end--------");
}
public void startdocument() throws saxexception {
system.out.println("------parse begin--------");
}
public void startelement(string p0, string p1, string p2, attributes p3) throws saxexception {
tags.push(p1);
}
public void endelement(string p0, string p1, string p2) throws saxexception {
tags.pop();
//一个link节点的信息收集齐了,将其格式化输出
if (p1.equals("link")) printout();
}
public void characters(char[] p0, int p1, int p2) throws saxexception {
//从栈中得到当前节点的信息
string tag=(string) tags.peek();
if (tag.equals("text")) text=new string(p0,p1,p2);
else if (tag.equals("url")) url=new string(p0,p1,p2);
else if (tag.equals("author")) author=new string(p0,p1,p2);
else if (tag.equals("day")) day=new string(p0,p1,p2);
else if (tag.equals("month")) month=new string(p0,p1,p2);
else if (tag.equals("year")) year=new string(p0,p1,p2);
else if (tag.equals("description")) year=new string(p0,p1,p2);
}
private void printout(){
system.out.print("content: ");
system.out.println(text);
system.out.print("url: ");
system.out.println(url);
system.out.print("author: ");
system.out.println(author);
system.out.print("date: ");
system.out.println(day+"-"+month+"-"+year);
system.out.print("description: ");
system.out.println(description);
system.out.println();
}
static public void main(string[] args) {
string filename = null;
boolean validation = false;
filename="links.xml";
saxparserfactory spf = saxparserfactory.newinstance();
saxparser saxparser=null;
try {
saxparser = spf.newsaxparser();
} catch (exception ex) {
system.err.println(ex);
system.exit(1);
}
try {
saxparser.parse(new file(filename),new saxreader());
} catch (saxexception se) {
system.err.println(se.getmessage());
system.exit(1);
} catch (ioexception ioe) {
system.err.println(ioe);
system.exit(1);
}
}
} 

     在这儿虽然没有使用到栈的分析,但实际上栈的分析是一件很容易的事情,应为java.util.stack继承了java.util.vector类,而且stack中的元素是按栈的结构由底至上排列的,因个,我们可以使用vector类的size()方法来得到stack的元素个数,还可以使用vector的get(int)方法来得到具体的每一个元属。实际上,如果把stack的元素从底向上逐一排列出来,我们就得到了从xml根节点到当前节点的一条唯一的路径,有了这条路径的信息,文档的结构就在清楚不过了。 

    

    小节 

    好了,到这儿为止,我们已经掌握了对于xml编程的两大利器:dom和sax,也知道了该如何在一个java程序中使用它们。dom编程相对简单,但是速度比较慢,占用内存多,而sax编程复杂一些,但是速度快,占用内存少。所以,我们应该根据不同的环境选择使用不同的方法。大部分的xml应用基本都可以用它们来解决。需要特别说明的是,dom和sax其实都是语言无关的,并非java所独有,也就是说,只要有相应的语言实现,dom和sax可以应用在任何面向对象的语言中。 

    上面我们介绍了xml文档的读入,内容提取,以及文档添加和修改的一些方法。还有一类的问题就是xml文档的转换,虽然用dom和sax也可以解决,但是实现起来很复杂,而应用xslt就会简单许多。这个问题,笔者将会在以后的文章中再和大家详细讨论。 

java与xml联合编程之dom篇

java与xml联合编程之dom篇  

java与xml联合编程之dom篇  
文章出处:www.ccidnet.com 作者:扬帆     发布时间:2004-12-31   点击:0
 

   dom初步 
  dom是document object model的缩写,即文档对象模型。前面说过,xml将数据组织为一颗树,所以dom就是对这颗树的一个对象描叙。通俗的说,就是通过解析xml文档,为xml文档在逻辑上建立一个树模型,树的节点是一个个对象。我们通过存取这些对象就能够存取xml文档的内容。 

  下面我们来看一个简单的例子,看看在dom中,我们是如何来操作一个xml文档的。 

  这是一个xml文档,也是我们要操作的对象: 

<?xml version="1.0" encoding="utf-8"?>
<messages>
<message>good-bye serialization, hello java!</message>
</messages> 


  下面,我们需要把这个文档的内容解析到一个个的java对象中去供程序使用,利用jaxp,我们只需几行代码就能做到这一点。首先,我们需要建立一个解析器工厂,以利用这个工厂来获得一个具体的解析器对象: 

documentbuilderfactory dbf = documentbuilderfactory.newinstance(); 


  我们在这里使用documentbuilderfacotry的目的是为了创建与具体解析器无关的程序,当documentbuilderfactory类的静态方法newinstance()被调用时,它根据一个系统变量来决定具体使用哪一个解析器。又因为所有的解析器都服从于jaxp所定义的接口,所以无论具体使用哪一个解析器,代码都是一样的。所以当在不同的解析器之间进行切换时,只需要更改系统变量的值,而不用更改任何代码。这就是工厂所带来的好处。这个工厂模式的具体实现,可以参看下面的类图。 

documentbuilder db = dbf.newdocumentbuilder(); 


  当获得一个工厂对象后,使用它的静态方法newdocumentbuilder()方法可以获得一个documentbuilder对象,这个对象代表了具体的dom解析器。但具体是哪一种解析器,微软的或者ibm的,对于程序而言并不重要。 

  然后,我们就可以利用这个解析器来对xml文档进行解析了: 

document doc = db.parse("c:/xml/message.xml"); 


  documentbuilder的parse()方法接受一个xml文档名作为输入参数,返回一个document对象,这个document对象就代表了一个xml文档的树模型。以后所有的对xml文档的操作,都与解析器无关,直接在这个document对象上进行操作就可以了。而具体对document操作的方法,就是由dom所定义的了。 
          


  jaxp支持w3c所推荐的dom 2。如果你对dom很熟悉,那么下面的内容就很简单了:只需要按照dom的规范来进行方法调用就可以。当然,如果你对dom不清楚,也不用着急,后面我们会有详细的介绍。在这儿,你所要知道并牢记的是:dom是用来描叙xml文档中的数据的模型,引入dom的全部原因就是为了用这个模型来操作xml文档的中的数据。dom规范中定义有节点(即对象)、属性和方法,我们通过这些节点的存取来存取xml的数据。 

  从上面得到的document对象开始,我们就可以开始我们的dom之旅了。使用document对象的getelementsbytagname()方法,我们可以得到一个nodelist对象,一个node对象代表了一个xml文档中的一个标签元素,而nodelist对象,观其名而知其意,所代表的是一个node对象的列表: 

nodelist nl = doc.getelementsbytagname("message"); 


  我们通过这样一条语句所得到的是xml文档中所有<message>标签对应的node对象的一个列表。然后,我们可以使用nodelist对象的item()方法来得到列表中的每一个node对象: 

node my_node = nl.item(0); 


  当一个node对象被建立之后,保存在xml文档中的数据就被提取出来并封装在这个node中了。在这个例子中,要提取message标签内的内容,我们通常会使用node对象的getnodevalue()方法: 

string message = my_node.getfirstchild().getnodevalue(); 


  请注意,这里还使用了一个getfirstchild()方法来获得message下面的第一个子node对象。虽然在message标签下面除了文本外并没有其它子标签或者属性,但是我们坚持在这里使用getfirsechild()方法,这主要和w3c对dom的定义有关。w3c把标签内的文本部分也定义成一个node,所以先要得到代表文本的那个node,我们才能够使用getnodevalue()来获取文本的内容。 

  现在,既然我们已经能够从xml文件中提取出数据了,我们就可以把这些数据用在合适的地方,来构筑应用程序。 

  下面的内容,我们将更多的关注dom,为dom作一个较为详细的解析,使我们使用起来更为得心应手。 

  dom详解 
  1.基本的dom对象 
  dom的基本对象有5个:document,node,nodelist,element和attr。下面就这些对象的功能和实现的方法作一个大致的介绍。 
           


  document对象代表了整个xml的文档,所有其它的node,都以一定的顺序包含在document对象之内,排列成一个树形的结构,程序员可以通过遍历这颗树来得到xml文档的所有的内容,这也是对xml文档操作的起点。我们总是先通过解析xml源文件而得到一个document对象,然后再来执行后续的操作。此外,document还包含了创建其它节点的方法,比如createattribut()用来创建一个attr对象。它所包含的主要的方法有: 

  createattribute(string):用给定的属性名创建一个attr对象,并可在其后使用setattributenode方法来放置在某一个element对象上面。 

  createelement(string):用给定的标签名创建一个element对象,代表xml文档中的一个标签,然后就可以在这个element对象上添加属性或进行其它的操作。 

  createtextnode(string):用给定的字符串创建一个text对象,text对象代表了标签或者属性中所包含的纯文本字符串。如果在一个标签内没有其它的标签,那么标签内的文本所代表的text对象是这个element对象的唯一子对象。 

  getelementsbytagname(string):返回一个nodelist对象,它包含了所有给定标签名字的标签。 

  getdocumentelement():返回一个代表这个dom树的根节点的element对象,也就是代表xml文档根元素的那个对象。 

  node对象是dom结构中最为基本的对象,代表了文档树中的一个抽象的节点。在实际使用的时候,很少会真正的用到node这个对象,而是用到诸如element、attr、text等node对象的子对象来操作文档。node对象为这些对象提供了一个抽象的、公共的根。虽然在node对象中定义了对其子节点进行存取的方法,但是有一些node子对象,比如text对象,它并不存在子节点,这一点是要注意的。node对象所包含的主要的方法有: 

  appendchild(org.w3c.dom.node):为这个节点添加一个子节点,并放在所有子节点的最后,如果这个子节点已经存在,则先把它删掉再添加进去。 

  getfirstchild():如果节点存在子节点,则返回第一个子节点,对等的,还有getlastchild()方法返回最后一个子节点。 

  getnextsibling():返回在dom树中这个节点的下一个兄弟节点,对等的,还有getprevioussibling()方法返回其前一个兄弟节点。 

  getnodename():根据节点的类型返回节点的名称。 

  getnodetype():返回节点的类型。 

  getnodevalue():返回节点的值。 

  haschildnodes():判断是不是存在有子节点。 

  hasattributes():判断这个节点是否存在有属性。 

  getownerdocument():返回节点所处的document对象。 

  insertbefore(org.w3c.dom.node new,org.w3c.dom.node ref):在给定的一个子对象前再插入一个子对象。 

  removechild(org.w3c.dom.node):删除给定的子节点对象。 

  replacechild(org.w3c.dom.node new,org.w3c.dom.node old):用一个新的node对象代替给定的子节点对象。 

  nodelist对象,顾名思义,就是代表了一个包含了一个或者多个node的列表。可以简单的把它看成一个node的数组,我们可以通过方法来获得列表中的元素: 

  getlength():返回列表的长度。 

  item(int):返回指定位置的node对象。 

  element对象代表的是xml文档中的标签元素,继承于node,亦是node的最主要的子对象。在标签中可以包含有属性,因而element对象中有存取其属性的方法,而任何node中定义的方法,也可以用在element对象上面。 

  getelementsbytagname(string):返回一个nodelist对象,它包含了在这个标签中其下的子孙节点中具有给定标签名字的标签。 

  gettagname():返回一个代表这个标签名字的字符串。 

  getattribute(string):返回标签中给定属性名称的属性的值。在这儿需要主要的是,应为xml文档中允许有实体属性出现,而这个方法对这些实体属性并不适用。这时候需要用到getattributenodes()方法来得到一个attr对象来进行进一步的操作。 

  getattributenode(string):返回一个代表给定属性名称的attr对象。 

  attr对象代表了某个标签中的属性。attr继承于node,但是因为attr实际上是包含在element中的,它并不能被看作是element的子对象,因而在dom中attr并不是dom树的一部分,所以node中的getparentnode(),getprevioussibling()和getnextsibling()返回的都将是null。也就是说,attr其实是被看作包含它的element对象的一部分,它并不作为dom树中单独的一个节点出现。这一点在使用的时候要同其它的node子对象相区别。 

  需要说明的是,上面所说的dom对象在dom中都是用接口定义的,在定义的时候使用的是与具体语言无关的idl语言来定义的。因而,dom其实可以在任何面向对象的语言中实现,只要它实现了dom所定义的接口和功能就可以了。同时,有些方法在dom中并没有定义,是用idl的属性来表达的,当被映射到具体的语言时,这些属性被映射为相应的方法。 

  2.dom实例 
  有了上面的介绍,相信你对dom理解的更多了吧。下面的例子将让你对dom更加熟悉起来。 

  先说说这个例子到底要做的是什么吧,我们希望在一个名为link.xml文件中保存了一些url地址,通过一个简单的程序,我们可以通过dom把这些url读出并显示出来,也可以反过来向这个xml文件中写入加入的url地址。很简单,却很实用,也足够来例示dom的绝大部分用法了。 

  xml文件本身不复杂,就不给出它的dtd了。link.xml: 

<?xml version="1.0" standalone="yes"?>
<links>
<link>
<text>jsp insider</text>
<url newwindow="no">http://www.jspinsider.com</url>
<author>jsp insider</author>
<date>
<day>2</day>
<month>1</month>
<year>2001</year>
</date>
<description>a jsp information site.</description>
</link>
<link>
<text>the makers of java</text>
<url newwindow="no">http://java.sun.com</url>
<author>sun microsystems</author>
<date>
<day>3</day>
<month>1</month>
<year>2001</year>
</date>
<description>sun microsystem's website.</description>
</link>
<link>
<text>the standard jsp container</text>
<url newwindow="no">http://jakarta.apache.org</url>
<author>apache group</author>
<date>
<day>4</day>
<month>1</month>
<year>2001</year>
</date>
<description>some great software.</description>
</link>
</links> 


  第一个程序我们称为xmldisplay.java,具体的程序清单可以在附件中找到。主要的功能就是读取这个xml文件中各个节点的内容,然后在格式化输出在system.out上,我们来看看这个程序: 

import javax.xml.parsers.*;
import org.w3c.dom.*; 


  这是引入必要的类,因为在这里使用的是sun所提供的xml解析器,因而需要引入java.xml.parsers包,其中包含了有dom解析器和sax解析器的具体实现。org.w3c.dom包中定义了w3c所制定的dom接口。 

documentbuilderfactory factory = documentbuilderfactory.newinstance();
documentbuilder builder=factory.newdocumentbuilder();
document doc=builder.parse("links.xml");
doc.normalize(); 


  除了上面讲到的,还有一个小技巧,对document对象调用normalize(),可以去掉xml文档中作为格式化内容的空白而映射在dom树中的不必要的text node对象。否则你得到的dom树可能并不如你所想象的那样。特别是在输出的时候,这个normalize()更为有用。 

nodelist links =doc.getelementsbytagname("link"); 


  刚才说过,xml文档中的空白符也会被作为对象映射在dom树中。因而,直接调用node方法的getchildnodes方法有时候会有些问题,有时不能够返回所期望的nodelist对象。解决的办法是使用element的getelementbytagname(string),返回的nodelise就是所期待的对象了。然后,可以用item()方法提取想要的元素。 

for (int i=0;i<links.getlength();i++){
element link=(element) links.item(i);
system.out.print("content: ");
system.out.println(link.getelementsbytagname("text").item(0).getfirstchild().getnodevalue());
system.out.print("url: ");
system.out.println(link.getelementsbytagname("url").item(0).getfirstchild().getnodevalue());
system.out.print("author: ");
system.out.println(link.getelementsbytagname("author").item(0).getfirstchild().getnodevalue());
system.out.print("date: ");
element linkdate=(element) link.getelementsbytagname("date").item(0);
string day=linkdate.getelementsbytagname("day").item(0).getfirstchild().getnodevalue();
string month=linkdate.getelementsbytagname("month").item(0).getfirstchild().getnodevalue();
string year=linkdate.getelementsbytagname("year").item(0).getfirstchild().getnodevalue();
system.out.println(day+"-"+month+"-"+year);
system.out.print("description: ");
system.out.println(link.getelementsbytagname("description").item(0).getfirstchild().getnodevalue());
system.out.println();
} 


  上面的代码片断就完成了对xml文档内容的格式化输出。只要注意到一些细节的问题,比如getfirstchile()方法和getelementsbytagname()方法的使用,这些还是比较容易的。 

  下面的内容,就是在修改了dom树后重新写入到xml文档中去的问题了。这个程序名为xmlwrite.java。在jaxp1.0版本中,并没有直接的类和方法能够处理xml文档的写入问题,需要借助其它包中的一些辅助类。而在jaxp1.1版本中,引入了对xslt的支持,所谓xslt,就是对xml文档进行变换(translation)后,得到一个新的文档结构。利用这个新加入的功能,我们就能够很方便的把新生成或者修改后的dom树从新写回到xml文件中去了,下面我们来看看代码的实现, 这段代码的主要功能是向links.xml文件中加入一个新的link节点: 

import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.domsource;
import javax.xml.transform.stream.streamresult;
import org.w3c.dom.*; 


  新引入的java.xml.transform包中的几个类,就是用来处理xslt变换的。 

  我们希望在上面的xml文件中加入一个新的link节点,因而首先还是要读入links.xml文件,构建一个dom树,然后再对这个dom树进行修改(添加节点),最后把修改后的dom写回到links.xml文件中: 

documentbuilderfactory factory = documentbuilderfactory.newinstance();
documentbuilder builder=factory.newdocumentbuilder();
document doc=builder.parse("links.xml");
doc.normalize();
//---取得变量----
string text="hanzhong's homepage";
string url="www.hzliu.com";
string author="hzliu liu";
string discription="a site from hanzhong liu, give u lots of suprise!!!"; 


  为了看清重点,简化程序,我们把要加入的内容硬编码到记忆string对象中,而实际操作中,往往利用一个界面来提取用户输入,或者通过jdbc从数据库中提取想要的内容。 

text textseg;
element link=doc.createelement("link"); 


  首先应该明了的是,无论什么类型的node,text型的也好,attr型的也好,element型的也好,它们的创建都是通过document对象中的createxxx()方法来创建的(xxx代表具体要创建的类型),因此,我们要向xml文档中添加一个link项目,首先要创建一个link对象: 

element linktext=doc.createelement("text");
textseg=doc.createtextnode(text);
linktext.appendchild(textseg);
link.appendchild(linktext);
element linkurl=doc.createelement("url");
textseg=doc.createtextnode(url);
linkurl.appendchild(textseg);
link.appendchild(linkurl);
element linkauthor=doc.createelement("author");
textseg=doc.createtextnode(author);
linkauthor.appendchild(textseg);
link.appendchild(linkauthor);
java.util.calendar rightnow = java.util.calendar.getinstance();
string day=integer.tostring(rightnow.get(java.util.calendar.day_of_month));
string month=integer.tostring(rightnow.get(java.util.calendar.month));
string year=integer.tostring(rightnow.get(java.util.calendar.year));
element linkdate=doc.createelement("date");
element linkdateday=doc.createelement("day");
textseg=doc.createtextnode(day);
linkdateday.appendchild(textseg);
element linkdatemonth=doc.createelement("month");
textseg=doc.createtextnode(month);
linkdatemonth.appendchild(textseg);
element linkdateyear=doc.createelement("year");
textseg=doc.createtextnode(year);
linkdateyear.appendchild(textseg);
linkdate.appendchild(linkdateday);
linkdate.appendchild(linkdatemonth);
linkdate.appendchild(linkdateyear);
link.appendchild(linkdate);
element linkdiscription=doc.createelement("description");
textseg=doc.createtextnode(discription);
linkdiscription.appendchild(textseg);
link.appendchild(linkdiscription); 


  创建节点的过程可能有些千篇一律,但需要注意的地方是,对element中所包含的text(在dom中,这些text也是代表了一个node的,因此也必须为它们创建相应的node),不能直接用element对象的setnodevalue()方法来设置这些text的内容,而需要用创建的text对象的setnodevalue()方法来设置文本,这样才能够把创建的element和其文本内容添加到dom树中。看看前面的代码,你会更好的理解这一点: 

doc.getdocumentelement().appendchild(link); 


  最后,不要忘记把创建好的节点添加到dom树中。document类的getdocumentelement()方法,返回代表文档根节点的element对象。在xml文档中,根节点一定是唯一的。 

transformerfactory tfactory =transformerfactory.newinstance();
transformer transformer = tfactory.newtransformer();
domsource source = new domsource(doc);
streamresult result = new streamresult(new java.io.file("links.xml"));
transformer.transform(source, result); 


  然后就是用xslt把dom树输出了。这里的transformerfactory也同样应用了工厂模式,使得具体的代码同具体的变换器无关。实现的方法和documentbuilderfactory相同,这儿就不赘述了。transformer类的transfrom方法接受两个参数、一个数据源source和一个输出目标result。这里分别使用的是domsource和streamresult,这样就能够把dom的内容输出到一个输出流中,当这个输出流是一个文件的时候,dom的内容就被写入到文件中去了。 

DOM编程详解

以Sun公司的JAXP为例来看看其中和DOM相关的包:

DOM包结构

   org.w3c.com:定义了DOM的接口。这是w3c所指制定的DOM规范,因为DOM规范是与语言无关的,因而其中只是定义了接口,而没有实现任何地类。任何具体的DOM实现需要有其它的类库给出。

    javax.xml.parser:定义了DocumentBuilderFactory类DocumentBuilder类。编程时用 DocumentBuilderFactory来生成一个具体的和具体的类库相联系的DocumentBuilder类的实例,然后再由这个 DocumentBuilder对象来生成一个Document实例。Document对象中就包含了DOM的树模型结构,是DOM模型的根。同 SAXParserFactory一样,具体的DocumentBuilder对象的建立,取决于一个环境变量 javax.xml.parsers.DocumentBuilderFactory的值,同样的,也可以直接把类名传递给 DocumentBuilderFactory来建立一个DocumentBuilder。


    com.sun.xml.tree:Sun的Java XML的DOM实现,他对标准的DOM提供了一些扩展,其中大部分的东西并不是DOM标准。包括了XMLDocument,ElementNode, XMLDocumentBuilder和TreeWalker等类。XMLDocument类实现了DOM的Document界面,它同样也提供了静态的 工厂方法来建立一个Document对象。ElementNode类代表了在一个DOM树中的每一个节点元素,并且它经常被继承,来实现一些自定的功能。 而XMLDocumentBuilder实际上是一个DocumentHandler类型的类,也就是说,它接受一个SAX解析器传递过来的事件,然后根 据这些事件来在内存中建立一个DOM模型。

DOM处理过程

   DOM的处理过程相对SAX而言要简单些,先用DocumentBuilderFactory的静态方法newInstance()建立一个 DocumentBuilderFactory的实例,然后用它的newDocumentBuilder()方法建立一个 DocumentBuilder。然后可以用DocumentBuilder的parser()方法来解析一个XML文档并建立DOM模型。在JAXP中 还提供了更为方便的功能扩展,就是使用XMLDocument。你可以为一个SAX解析器注册一个事件处理器XMLDocumentBuilder,在解 析过后,可以调用XMLDocumentBuilder的getDocument()方法就可以把一个外在地XML文档转换成一个内存中的DOM树了,并 生成一个Document(XMLDocument)对象,后面的工作,就是调用XMLDocument对象和ElementNode对象的各个方法来对 DOM树进行操作了。最后,还可以调用XMLDocument的wirte()方法来把DOM树输出为XML文件。因为在标准的DOM模型中并没有提供与 write()相类似的方法,所以要处理输出的话,用XMLDocument会更方便些。

实例DOM

   下面我们可以来看看例子了。

首先当然是import语句:


import javax.xml.parsers.DocumentBuilderFactory; 

import javax.xml.parsers.FactoryConfigurationError; 

import javax.xml.parsers.ParserConfigurationException;

import javax.xml.parsers.DocumentBuilder;
   为了能给你更多的印象,我详细内出了所有的类,实际上你只需要一句就可以了:

import javax.xml.parsers.*;

你还需要的是W3C对DOM和DOM例外的定义:

import org.w3c.dom.Document; import org.w3c.dom.DOMException;
还有一些其他的用来进行例外和I/O处理的类:


import org.xml.sax.SAXException; 

import org.xml.sax.SAXParseException;

import java.io.File;

import java.io.IOException;



因为要输出XML文档,所以还要引入XMLDocument:



import com.sun.xml.tree.XmlDocument; 

public class DomEcho{



在程序逻辑中,因为要处理DOM模型,所以当然首先应该申明一个Document对象:



static Document document;

public static void main (String argv [])

{

if (argv.length != 1) {

            System.err.println ("Usage: java DomEcho filename";

            System.exit (1);

        }
    用DocumentBuilderFactory类的静态方法newInstance()来创建一个工厂实例,之所以称为工厂,是由于再这儿应用到了设计 模式(Design Pattern)中的工厂模式,实际上在Java类库中设计模式的范例随处可见,如果能够对设计模式有一些了解,就会很容易为Java庞杂的类库理出一条 条脉络分明的经线。
        DocumentBuilderFactory factory =

           DocumentBuilderFactory.newInstance();

        try {

           DocumentBuilder builder = factory.newDocumentBuilder();

           document = builder.parse( new File(argv[0]) );
    前面说过,DOM标准模型中并没有定义输出的接口,因而如果需要进行文档输出的话,就需要用到JAXP的扩展,使用到XmlDocument。这儿有一个 较为简便的方法,就是用强制类型转换,把一个Document类转换成XmlDocument,然后,就可以使用XmlDocument的write() 方法了:
           XmlDocument xdoc = (XmlDocument) document;

           xdoc.write (System.out);
   在后面的都是例外的处理了,在DOM中虽然有定义了DOMException,但是DOMException只是用在遍历和操作DOM树时引发的例外。在解析文档和初始化解析器时所引发的例外,还是借助于SAX中的例外处理方法,以使程序上具有一致性:

        } catch (SAXParseException spe) {

             // 处理解析过程中生成的例外

           System.out.println ("\n** Parsing error"

              + ", line " + spe.getLineNumber ()

              + ", uri " + spe.getSystemId ());

           System.out.println("   " + spe.getMessage() );

           Exception  x = spe;

           if (spe.getException() != null)

               x = spe.getException();

           x.printStackTrace();

        } catch (SAXException sxe) {

           // 处理一般的程序例外或者解析器初始化时引发的例外

           Exception  x = sxe;

           if (sxe.getException() != null)

               x = sxe.getException();

           x.printStackTrace();

        } catch (IOException ioe) {

           // IO例外

           ioe.printStackTrace();

        }

    }

   这而对DOM讲的比较简略,但是基本的程序结构我们已经可以从中看出来了。