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

在Java中解析XML  _职场

 

刚才所建立的自定义标记非常的简单,只是输出一个规定的内容。在此我们可以对此标记进行扩展。增加主体,这样我们可以灵活的输出我们想要输出的内容。另外增加一个标记的属性,这样可以按照我们的要求输出任意数量的主体内容

同样需要建立标记类,实现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>

运行结果如下:

在Java中解析XML  _xml_02以上只是讲解自定义标记的简单开发过程,如果开发复杂功能的标记,可以按照自己的业务进行扩展。但是开发流程是通用的。后面我会写一个实用性的自定义标记的例子。

 

 引用地址:http://goldcreate.blog.sohu.com