综述:可扩展标注语言(extensible markup language,xml)正被迅速的运用于业界,它已作为与平台、语言和协议无关的格式描述和交换数据的广泛应用标准。xml和它的辅助规范可用于描述数据的文档表现,描述xml文档类型的限制,描述xml文档和资源之间的链接,描述xml文档的自动转换和格式化。
如何开发自定义标签库?
我使用jsp和asp编程已经有一段颇长的时间了,在两种服务器端的编程方式中,我越来越觉得jsp的功能要强大得多。不提别的,其中jsp的标签库就是我选择jsp作为首选服务器端web应用开发工具的原因。为什么?因为:维护和开发的速度。在一个单一的服务器页面中,你可以混合使用各种不同的脚本方法和对象。就?quot;混凝土"一样,这种混合可令服务器端的脚本变得强大,并且让服务器端的编程者设计出非常灵活和动态的web页面。不过这种自由的混合也有其缺点,那就是维护起来非常麻烦,特别是当项目逐渐变大时。由于最终的产品是经由一个传统的web设计者来维护的,因此会带来问题。更糟糕的是,随着代码的复杂性增加,开发的速度就会变慢,不利于开发中等和大型的web应用,一旦开发完,站点还要找合格的编程者来维护这些颇为复杂的代码。
幸好,jsp提供了一个很好解决的办法。标签库提供了一个简单的方法来建立一个可重用的代码块。一旦标签库设计好,它就可以在许多项目中再次使用。更方便的是,与com和j2ee不同,你无需学习任何其它的技巧就可以建立一个标签库!只要你懂得写jsp,你就可以建立一个标签库。标签库还可以改善web应用的维护。这个是得益于jsp页面自定义标签的简单xml接口。这样,web设计者甚至可以做到无需知道任何jsp的知识,就可以建立jsp的web应用。这个开放式的web开发对于团队运作是非常有效的。jsp编程者可以建立自定义的标签和后台的代码模块,而web设计者可以使用自定义的标签来建立web应用,并且将精力集中在web设计上。
1. 标签库的定义
jsp标签库(也称自定义库)可看成是一套产生基于xml脚本的方法,它经由javabeans来支持。在概念上说,标签库是非常简单和可以重用的代码构造。
执行xml/xsl转换的标签范例和html页面
| <%@ taglib uri="http://www.jspinsider.com/jspkit/jaxp" prefix="jaxp"%> c:/xml/example.xml c:/xml/example.xsl |
| /* html_format.java */ public class html_format extends object implements java.io.serializable { /** 创建新的html_format */ public html_format() {} /** 将一个字符串中所有的所有 < 和 > 字符用响应的html编码代替 */ public string html_encode(string as_data) { int li_len = as_data.length(); /*string buffer的长度要比原来的字符串长*/ stringbuffer lsb_encode = new stringbuffer(li_len + (li_len/10)); /* 循环替换全部的< 和 > 字符 */ for( int li_count = 0 ; li_count < li_len ; li_count++) { string ls_next = string.valueof(as_data.charat(li_count)); if (ls_next.equals("<")) ls_next = "<"; if (ls_next.equals(">")) ls_next = ">"; lsb_encode.append( ls_next ); } return( lsb_encode.tostring() ); } } |
| html编码标签处理器 import java.io.ioexception; import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; public class html_formattag extends bodytagsupport { /* 1} 在标签末将会调用这个函数 */ public int doendtag() throws jsptagexception { try { /* 2}得到标签中的文本 */ bodycontent l_tagbody = getbodycontent(); string ls_output = ""; /* 3}如果标签体有文本,就处理它 */ if(l_tagbody != null) { html_format l_format = new html_format(); /* 3a} 将标签体的内容转换为一个字符串 */ string ls_html_text = l_tagbody.getstring(); ls_output = l_format.html_encode(ls_html_text); } /* 4}将结果写回到数据流中 */ pagecontext.getout().write(ls_output.trim()); } catch (ioexception e) { throw new jsptagexception("tag error:" + e.tostring()); } /* 让jsp继续处理以下页面的内容 */ return eval_page; } } |
| html编码标签描述器 <?xml version="1.0" encoding="utf-8" ?> <!doctype taglib public "-//sun microsystems, inc.//dtd jsp tag library 1.1//en" "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd"> <taglib> <tlibversion>1.0</tlibversion> <jspversion>1.1</jspversion> <shortname>html_formattag</shortname> <uri></uri> <info>html encoding tag </info> <tag> <name>htmlencode</name> <tagclass>html_formattag</tagclass> <info>encode html</info> </tag> </taglib> |
| 修改web.xml文件 <?xml version="1.0" encoding="iso-8859-1"?> <!doctype web-app public "-//sun microsystems, inc.//dtd web application 2.2//en" "http://java.sun.com/j2ee/dtds/web-app_2.2.dtd"> <web-app> <taglib> <taglib-uri> htmlencode </taglib-uri> <taglib-location> /web-inf/html_formattag.tld </taglib-location> </taglib> </web-app> |
| <%@ taglib uri="htmlencode" prefix="examples" %> <pre> <?xml:namespace prefix = examples /><examples:htmlencode> < hello , simple sample > </examples:htmlencode> </pre> 范例代码的输出 < hello , simple sample > which displays as: < hello , simple sample > |
通过这个标签,我就将该页面的所有代码编码了。有趣的是所有的自定义标签都是在服务器上处理的。这意味着你将不会在输出的页面上看到自定义的标签。
建立一个标签不是很难吧。最困难的部分是要学习标签处理的所有细节。这是一个很强大的功能,我们只是提到了最基本的地方。由于这个处理需要几步,新的jsp编程者在创建标签时将会感到迷惑。
| <?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所定义的了。
从得到的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实例
先说说这个例子到底要做的是什么吧,我们在一个名为link.xml文件中保存了一些url地址,我们希望可以通过dom把这些url读出并显示出来,也可以反过来向这个xml文件中写入加入的url地址。很简单,却很实用,也足够来例示dom的绝大部分用法了。
第一个程序我们称为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()); …… |
上面的代码片断就完成了对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中所包含的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的内容就被写入到文件中去了。
| <poem> <author>ogden nash</author> <title>fleas</title> <line>adam</line> </poem> |
| {文档开始} startdocument() <poem> startelement(null,"poem",null,{attributes}) "\n" characters("<poem>\n...", 6, 1) <author> startelement(null,"author",null,{attributes}) "ogden nash" characters("<poem>\n...", 15, 10) </author> endelement(null,"author",null) "\n" characters("<poem>\n...", 34, 1) <title> startelement(null,"title",null,{attributes}) "fleas" characters("<poem>\n...", 42, 5) </title> endelement(null,"title",null) "\n" characters("<poem>\n...", 55, 1) <line> startelement(null,"line",null,{attributes}) "adam" characters("<poem>\n...", 62, 4) </line> endelement(null,"line",null) "\n" characters("<poem>\n...", 67, 1) </poem> endelement(null,"poem",null) {文档结束} enddocument() |
| 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.*; |
| 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; …… |
| public class saxreader extends defaulthandler { java.util.stack tags=new java.util.stack(); …… |
Java Asp PHP .Net XML C/C++ CGI VB Jsp J2ee J2se J2me EJB Servlet Tomcat Resin Struts Weblogic Eclipse ANT GUI JMS Web servise IDEA Webphere Hibernate Spring Jboss Applet Swing Socket Javamail Perl Ajax P2P 安全 模式 框架 测试 开源 游戏
Windows XP Windows 2000 Windows 2003 Windows Me Windows 9.x Linux UNIX 注册表 操作系统 服务器 应用服务器