XML在程序的开发中的地位越来越重要,我们经常的需要在编程中解析XML文档。在Java中解析XML文档有多种方式,每种方式都有它的特点。在此我们通过使用不同的解析方式解析XML文档来进行演示
首先是采用DOM(Document Object Model文档对象模型)的方式。采用此方式解析XML文档的时候,将XML文档整体加载,并形成文档对象树。文档中的所有内容都会形成树的节点(无论是元素、属性还是文本)。这样我们可以采用随机读取的方式进行读取。优点是我们解析文档时可以采用任意的顺序进行内容的操作,操作灵活。但是文档需要整体加载。所以当文档的内容比较大的时候,而且我们只需要读取其中的一小部分内容,显然是有问题的。因此,如果我们读取的xml文档相对较小时可以采用此方式。此方式编程也相对简单。(Ajax操作XML和Html一般采用此方式)
假设我们存在如下的xml文档,名称为:student.xml,我们通过DOM的方式解析文档中的内容
<?xml version="1.0" encoding="UTF-8"?>
<students>
<student>
<name>Tom</name>
<age>20</age>
<skill>java</skill>
</student>
<student>
<name>Mike</name>
<age>23</age>
<skill>.net</skill>
</student>
</students>
采用DOM解析上述文档的Java代码如下:
package com.frank.xml;
import java.io.File;
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class DOMSample {
/**
* @param args
* @throws IOException
* @throws SAXException
*/
public static void main(String[] args){
File file=new File("students.xml");
DocumentBuilderFactory fac=DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder;
try {
documentBuilder = fac.newDocumentBuilder();
Document document=documentBuilder.parse(file);
NodeList nodeList=document.getElementsByTagName("student");
for(int i=0;i<nodeList.getLength();i++){
//System.out.print(document.getElementsByTagName("name").item(i).getFirstChild().getNodeValue());
//System.out.print("===");
//System.out.print(document.getElementsByTagName("age").item(i).getFirstChild().getNodeValue());
//System.out.print("===");
//System.out.print(document.getElementsByTagName("skill").item(i).getFirstChild().getNodeValue());
System.out.print(nodeList.item(i).getChildNodes().item(1).getFirstChild().getNodeValue());
System.out.print("===");
System.out.print(nodeList.item(i).getChildNodes().item(3).getFirstChild().getNodeValue());
System.out.print("===");
System.out.print(nodeList.item(i).getChildNodes().item(5).getFirstChild().getNodeValue());
System.out.println("");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
输出结果如下:
Tom===20===java
Mike===23===.net
注意注释部分,同样可以完成解析文档的过程。为什么在实现的时候得到子节点用到的序号为1,3,5呢?因为每一个student拥有的子节点数为7而不是3,文本和student包含的文本(这里为空)同样是子节点。
采用SAX进行XML解析
采用SAX解析XML是顺序性的。就是自上而下解析XML文档。采用的是事件驱动机制。当遇到一个节点的时候马上进行处理,而不是等到文档整体加载后处理。SAX解析方式利用所提供的若干个方法。当遇到文档中的某部分时,利用回调机制调用相应的方法进行适当的处理。例如,SAX存在操作节点的方法:startElement和endElement
当遇到一个开始节点时调用startElement,后遇到一个结束节点时调用endElement。我们可以在这两个方法中写具体的处理过程实现对XML文档的解析,读取内容时利用所提供的另外一个方法characters
另外还有解析文档开始时候调用的startDocument和文档解析结束时调用endDocument
采用SAX解析不用整体加载文档,所以在解析比较大的文档的时候比较适合采用SAX方式。而且解析速度要优于DOM,但是操作过程编码要麻烦一些。
同样我们解析刚才的students.xml,利用SAX,可以如下:
package com.frank.xml;
import java.util.Stack;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class SAXSample extends DefaultHandler {
Stack tags = new Stack();
/**
* @param args
*/
public static void main(String[] args) {
try{
SAXParserFactory factory=SAXParserFactory.newInstance();
SAXParser sAXParser=factory.newSAXParser();
SAXSample sAXSample=new SAXSample();
sAXParser.parse(new InputSource("students.xml"), sAXSample);
}catch(Exception ex){
ex.printStackTrace();
}
}
@Override
public void characters(char ch[], int start, int length)
throws SAXException {
// TODO Auto-generated method stub
String tag = (String) tags.peek();
if(tag.equals("name")){
System.out.println("name="+new String(ch, start, length));
}
if(tag.equals("age")){
System.out.println("age="+new String(ch, start, length));
}
if(tag.equals("skill")){
System.out.println("skill="+new String(ch,start,length));
}
}
@Override
public void endElement(String arg0, String arg1, String arg2)
throws SAXException {
// TODO Auto-generated method stub
tags.pop();
}
@Override
public void startElement(String uri,String localName,String qName,Attributes attrs) throws SAXException {
// TODO Auto-generated method stub
tags.push(qName);
}
}
运行结果如下:
name=Tom
age=20
skill=java
name=Mike
age=23
skill=.net
标记的应用在Web编程中非常的基础也非常的重要。标记可以实现内容的现实、功能的调用。JSP标记其实就是一个在页面当中通过标记形式使用的java组件,可重拥、可以维护、便于扩展。很多框架中都开发出了大量的标记。例如Struts中的<iterator>标记就可以实现集合迭代的功能。JSP中本身就提供了标准的标记(动作)。例如useBean、include等标记。
当我们想完成自身系统的一些功能的时候,别人提供的标记可能不能完全满足我们的需要。这时可以开发自己的标记。然后在系统中重复使用。
标记的功能的实现根据各自系统业务的不同变化很大,但是我认为开发自定义标记最终要的就是明白开发流程,当你明白开发流程的时候,无论是简单的标记还是复杂功能实现的标记都没有任何的问题。那个时候我们只需要考虑功能如何实现了。开发自定义标记遵循如下的流程或步骤
建立标记类:这是组基本的,标记类就是一个具有标记特性的java类,所以要继承标记的父类或者实现具有标记特性的接口。然后在其中实现你的标记所需要完成的功能。标记类中的方法大部分为继承过来的(或实现接口)的契约方法。自动调用。但是一定要明白每种方法的用途和调用顺序。
其次就是建立标记描述文件(TLD文件):标记描述文件是一个XML文件。对标记进行各种描述。例如如何使用、标记的特性等等。就相当于一个标记的说明书。标记本身就是一个类,但是使用的时候要在页面中以标记的形式(<>)使用,如何使用就要按照这个说明书的描述去做了。
完成了上述工作,下面就是在具体的Web项目中使用了,所以需要在项目的web.xml文件中对TLD文件进行加载。使用taglib标记进行加载
最后就是实际的使用了,当然是在jsp中。利用taglib指令进行声明。
好了,明白了上述步骤我们就可以开始开发一个简单的标记了。非常的简单,就是利用标记在页面中显示一个规定的文本内容,在这里假设现实“My First Tag”。在此标记没有主体,所以只需要实现Tag接口(无主体接口)。
首先建立Web项目,然后建立标记类,实现Tag接口。代码如下:
package com.frank.tag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.Tag;
public class NoBodyTag implements Tag {
private PageContext pageContext;
public int doEndTag() throws JspException {
try{
this.pageContext.getOut().write("<h1>My First Tag</h1>");
}catch(IOException ex){
ex.printStackTrace();
}
return Tag.EVAL_PAGE;
}
public int doStartTag() throws JspException {
return Tag.SKIP_BODY;
}
public Tag getParent() {
// TODO Auto-generated method stub
return null;
}
public void release() {
// TODO Auto-generated method stub
}
public void setPageContext(PageContext pageContext) {
this.pageContext=pageContext;
}
public void setParent(Tag arg0) {
// TODO Auto-generated method stub
}
}
setPageContext方法用来得到以后标记所放置页面的环境,通过取得放置的页面环境(页面上下文)我们就可以在所在的页面中输出相应的内容。页面环境通过参数传入,所以在这里声明一个数据成员pageContext得到以后的页面环境。
doStartTag在标记开始执行时调用,返回SKIP_BODY代表忽略主体(不计算主体,因为此标记没有主体)。doEndTag方法在标记结束执行时调用,在此完成内容的输出工作,当然是输出到当前的页面(pageContext)。通过返回EVAL_PAGE代表继续执行页面的余下部分。
到此,标记类完成(很简单的)
下面就可以开始写标记的描述文件了,标记文件需要放置在WEB-INF目录下(可以在此目录建立自目录)。在此我的描述文件放置在WEB-INF/tags目录中,名称为mytag.tld。描述文件可以利用模板生成,如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_1.dtd" >
<taglib>
<tlibversion>1.1</tlibversion>
<jspversion>1.1</jspversion>
<shortname>mysampletag</shortname>
<uri>/mytags</uri>
<tag>
<name>firstTag</name>
<tagclass>com.frank.tag.NoBodyTag</tagclass>
<bodycontent>empty</bodycontent>
</tag>
</taglib>
其中shortname代表标记库的短名称。uri是标记库的命名空间名称,就是标记库的组织名称。利用这个名称我们可以引用此标记库。一个标记库可以描述多个标记。每一个标记的描述有<tag>标记对实现。
其中的name代表我们在页面中使用此标记的名称,tagclass代表对应的标记类,bodycontent用来描述主体的样式。在此无主体使用empty。
接下来就可以在web.xml中对标记库描述文件进行加载了,以便网站运行的时候可以找到标记库,通过刚才声明的uri名称进行定位查找
在web.xml中利用taglib标记对标记库描述文件进行加载,如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<jsp-config>
<taglib>
<taglib-uri>/mytags</taglib-uri>
<taglib-location>/WEB-INF/tags/mytag.tld</taglib-location>
</taglib>
</jsp-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
<taglib-uri>用来引用特定的标记库,利用uri名称引用,当然是刚才在tld中声明的/mytags。<taglib-location>用来描述标记库的路径
最后就可以在jsp中使用标记了,首先需要使用taglib指令进行声明,整体页面代码如下:
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%@ taglib uri="/mytags" prefix="m" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<m:firstTag/>
</body>
</html>
taglib指令中的uri属性同样需要指明标记库的命名空间名,prefix指明的是使用此标记库中的标记所要用的前缀(自己设定)
使用过程: <m:firstTag/>
部署运行,效果如下,每书写一次标记,在页面中输出固定文本内容My First Tag
刚才所建立的自定义标记非常的简单,只是输出一个规定的内容。在此我们可以对此标记进行扩展。增加主体,这样我们可以灵活的输出我们想要输出的内容。另外增加一个标记的属性,这样可以按照我们的要求输出任意数量的主体内容
同样需要建立标记类,实现BodyTag接口,这样标记可以带有主体,代码如下:
package com.frank.tag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTag;
import javax.servlet.jsp.tagext.Tag;
public class HaveBodyTag implements BodyTag {
private PageContext pageContext;
private BodyContent bodyContent;
private int count;
public void doInitBody() throws JspException {
// TODO Auto-generated method stub
}
public void setBodyContent(BodyContent bodyContent) {
this.bodyContent=bodyContent;
}
public int doAfterBody() throws JspException {
if(count>1){
count--;
return BodyTag.EVAL_BODY_BUFFERED;
}
return BodyTag.SKIP_BODY;
}
public int doEndTag() throws JspException {
try{
this.pageContext.getOut().write(bodyContent.getString());
}catch(IOException ex){
ex.printStackTrace();
}
return BodyTag.EVAL_PAGE;
}
public int doStartTag() throws JspException {
if(count>0){
return BodyTag.EVAL_BODY_BUFFERED;
}
return BodyTag.SKIP_BODY;
}
public Tag getParent() {
// TODO Auto-generated method stub
return null;
}
public void release() {
// TODO Auto-generated method stub
}
public void setPageContext(PageContext pageContext) {
this.pageContext=pageContext;
}
public void setParent(Tag t) {
// TODO Auto-generated method stub
}
public void setCount(int count) {
this.count = count;
}
}
其中setBodyContent方法用于获取主体内容。因为有主体,所以在doStartTag方法中计算主体,然后执行doAfterBody,如果返回BodyTag.EVAL_BODY_BUFFERED则继续执行doAfterBody,否则退出,执行doEndTag
doEndTag对计算的主体进行输出,计算多少次,输出多少次。
注意输出的数量count是通过属性传入的,所以标记中必须存在一个名称同样为count的属性
所以在标记库描述文件中加入新的标记的描述,并增加属性count,如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_1.dtd" >
<taglib>
<tlibversion>1.1</tlibversion>
<jspversion>1.1</jspversion>
<shortname>mysampletag</shortname>
<uri>/mytags</uri>
<tag>
<name>firstTag</name>
<tagclass>com.frank.tag.NoBodyTag</tagclass>
<bodycontent>empty</bodycontent>
</tag>
<tag>
<name>looptag</name>
<tagclass>com.frank.tag.HaveBodyTag</tagclass>
<bodycontent>scriptless</bodycontent>
<attribute>
<name>count</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
</taglib>
在此的主体为非脚本主体scriptless,属性是必须的required为true
web.xml因为加载的是同样的标记库文件,所以不用修改。同样jsp页面使用的也是同样的标记库,也不用修改。
只是在页面中使用looptag标记,并设置属性count,和你定义的主体内容,例如:
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%@ taglib uri="/mytags" prefix="m" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<m:looptag count="5">
<h1>New Content</h1><br>
</m:looptag>
</body>
</html>
运行结果如下:
以上只是讲解自定义标记的简单开发过程,如果开发复杂功能的标记,可以按照自己的业务进行扩展。但是开发流程是通用的。后面我会写一个实用性的自定义标记的例子。
引用地址:http://goldcreate.blog.sohu.com