Sax快速入门

一、本教程旨在为希望运用SAX的Java程序员提供一份快速入门的教程。

二、基本的环境

Sax是一个用于各种各样的XML解析器(或者其他可以看做XML解析器的解析器)通用接口的实现,就像JDBC作为一个能供许多关系型

数据库的通用驱动的一样。如果你想用SAX,你需用如下的几样东西:

a. Java 1.1或更高版本

b. 一个能兼用SAX2(总共有这么几个版本:SAX2M,2001-11-12; SAX 2.0.1, 2002-1-29, SAX2.0.2, 2004-4-24)的XML解析器被安装

在你的Java classpath中

c. SAX2被安装在你的java classpath中(貌似Jdk已经集成了吧?)

大多数的Java/XML工具包括SAX2 和 XML解析器都用到SAX。大多数的web应用服务器也把它最为XML support的核心。特别强调的是,

JAXP 1.1 支持SAX2.

三、XML的解析

我们从创建DefaultHandler的一个子类开始:

import org.xml.sax.helpers.DefaultHandler;

public class MySAXApp extends DefaultHandler
{

    public MySAXApp ()
    {
	super();
    }

}

因为这是一个Java程序,我们将会创建一个Main方法来调用XMLReaderFactory 的createXMLReader方法来动态地选择一个SAX

驱动。注意了,在这里,我们只用了“throws Exception”,在真正的应用程序中,将会有容错处理:

 public static void main (String args[])
	throws Exception
    {
	XMLReader xr = XMLReaderFactory.createXMLReader();
    }

可能在你的Java环境中还没有整合默认的编译路径(或者可能是 META-INF/services/org.xl.sax.driver  system resource),

那么你需要去为SAX driver设置 org.xml.sax.dirve 的Java 系统属性, 比如

java -Dorg.xml.sax.driver=com.example.xml.SAXDriver MySAXApp sample.xml
下面是击中SAX2的驱动,它们的class names和相关信息如下:

Class NameNotes
gnu.xml.aelfred2.SAXDriverLightweight non-validating parser; Free Software
gnu.xml.aelfred2.XmlReaderOptionally validates; Free Software
oracle.xml.parser.v2.SAXParserOptionally validates; proprietary
org.apache.crimson.parser.XMLReaderImplOptionally validates; used in JDK 1.4; Open Source
org.apache.xerces.parsers.SAXParserOptionally validates; Open Source


或者,如果你不想在你的程序中使用指定的Sax drive, 你也可以直接用它的构造器。我们假设你的XML解析器叫做com.example.xml.SAXDriver,

这实际上可能不存在。你必须知道要知道用于解析的那个真正driver的名字,这样,可能进行如下操作:

public static void main (String args[])
	throws Exception
    {
	XMLReader xr = new com.example.xml.SAXDriver();
    }
我们用xr这个对象去解析XML文档,但是,我们必须先去注册一个事件handlers,以便于解析器用来report信息,从XMLReader中调用setConentHandler 和

setErrorHandler方法,在一个实际程序的应用中,这个hanlders通常要做成一个独立的对象,但是在这里,只是为了演示,我们把handlers绑定

在了最顶层的类中(即main方法所在的类),这样,我们必须去实例化这个顶层类并且为XML Reader这个解析器注册hanlders:

public static void main (String args[])
	throws Exception
    {
	XMLReader xr = XMLReaderFactory.createXMLReader();
	MySAXApp handler = new MySAXApp();
	xr.setContentHandler(handler);
	xr.setErrorHandler(handler);
    }

上面的代码创建了MySAXApp的一个实例去接受XML解析事件,并且把常规的content 事件和error 事件处理器注册到这个解析器上。

现在,就让我们来开始解析吧。

public static void main (String args[])
	throws Exception
    {
	XMLReader xr = XMLReaderFactory.createXMLReader();
	MySAXApp handler = new MySAXApp();
	xr.setContentHandler(handler);
	xr.setErrorHandler(handler);

				// Parse each file provided on the
				// command line.
	for (int i = 0; i < args.length; i++) {
	    FileReader r = new FileReader(args[i]);
	    xr.parse(new InputSource(r));
	}
    }

值得注意的是,每一个reader必须被包装成一个inputSource而被解析。以下是到目前为止这个demo的整个代码:

import java.io.FileReader;

import org.xml.sax.XMLReader;
import org.xml.sax.InputSource;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.helpers.DefaultHandler;


public class MySAXApp extends DefaultHandler
{

    public static void main (String args[])
	throws Exception
    {
	XMLReader xr = XMLReaderFactory.createXMLReader();
	MySAXApp handler = new MySAXApp();
	xr.setContentHandler(handler);
	xr.setErrorHandler(handler);

				// Parse each file provided on the
				// command line.
	for (int i = 0; i < args.length; i++) {
	    FileReader r = new FileReader(args[i]);
	    xr.parse(new InputSource(r));
	}
    }


    public MySAXApp ()
    {
	super();
    }
}
你可以编译上述代码并且运行它。(需要确保的一点是,你因该在org.xml.sax.dirver property 文件中定义这个Sax driver class),如果

被解析的文档不是畸形的,这个程序将啥也不会出现(畸形的xml的话,会抛xml结构异常信息)。这是因为你还没为这个应用程序set up处理事件。

处理事件

当你开始去为XML解析实现响应方法时,这就会变得越来越有趣了。其中最重要的响应事件某过于文档的开始和结束,元素的开始和结束,字符数据

的处理。

为了找出document的start和end,这个应用程序必须实现startDocument和endDocument这两个方法:

 public void startDocument ()
    {
	System.out.println("Start document");
    }

    public void endDocument ()
    {
	System.out.println("End document");
    }

这个start/endDocument 的event handler没有参数。当Sax driver一旦到达document的开头的时候,它会调用startDocument方法,一但到达结尾时,它会调用endDocument方法(即使这个文档存在一些错误)。

这个demo只是简单地输出了一些消息,但是,你的程序也可以在这个handler里增加任何你想要的代码。通常地,这些被增加的代码回去做

下面的几件事情:在momery中生成一些dom 树,输出,整合到database中,或者从XML stream中提取信息。

Sax driver将会通过同document一样的方式标志一个元素的start和end,这两个方法中会用一些参数被传递过来:

 public void startElement (String uri, String name,
			      String qName, Attributes atts)
    {
	if ("".equals (uri))
	    System.out.println("Start element: " + qName);
	else
	    System.out.println("Start element: {" + uri + "}" + name);
    }

    public void endElement (String uri, String name, String qName)
    {
	if ("".equals (uri))
	    System.out.println("End element: " + qName);
	else
	    System.out.println("End element:   {" + uri + "}" + name);
    }

这个方法每次在遇到element start和end的时候,如果这个元素的namespace存在,它将会输出namespace和localname如果不存在namespace,

它将会输出它将会输出qName(qualified Name, 参考qName).

最后,SAX2通过characters这个方法显示一些常规的字符串数据。在下面的实现中,将会输出所有的字符数据(标签的文本数据)。代码可能有一点长

因为要编译一些特殊的字符:

public void characters (char ch[], int start, int length)
    {
	System.out.print("Characters:    \"");
	for (int i = start; i < start + length; i++) {
	    switch (ch[i]) {
	    case '\\':
		System.out.print("\\\\");
		break;
	    case '"':
		System.out.print("\\\"");
		break;
	    case '\n':
		System.out.print("\\n");
		break;
	    case '\r':
		System.out.print("\\r");
		break;
	    case '\t':
		System.out.print("\\t");
		break;
	    default:
		System.out.print(ch[i]);
		break;
	    }
	}
	System.out.print("\"\n");
    }

注意,因为SAX driver可以随意获取它想要的任意字符数据块,所以你不可能仅通过一个简单的characters事件就能计算出这个element的所有字符信息。

一下是关于characters的一个具体例子:

package com.aug.desmond.sax.start.test;

import java.io.FileReader;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

import static com.aug.desmond.util.Print.*;

public class MySAXApp extends DefaultHandler {
	public static void main(String[] args) throws Exception{
		XMLReader xr = XMLReaderFactory.createXMLReader();
		MySAXApp handler = new MySAXApp();
		xr.setContentHandler(handler);
		xr.setErrorHandler(handler);
		
		for(int i=0; i<args.length; i++) {
			FileReader r = new FileReader(args[i]);
			xr.parse(new InputSource(r));
		}
	}
	
	@Override
	public void characters(char ch[], int start, int length) throws SAXException {
		print(new String(ch, start, length));
	}


	@Override
	public void endElement(String uri, String localName, String qName)
			throws SAXException {
		print("[end element] "+uri+","+localName+","+qName);
	}


	@Override
	public void startElement(String uri, String localName,
		      String qName, Attributes attributes) throws SAXException {
		print("[start element] "+uri+","+localName+","+qName);
	}

	public MySAXApp() {
		super();
	}
}
XML文件:

<?xml version="1.0" encoding="UTF-8" ?>
<root>
	<head/>
	<body>world</body>out_of_tag_after_body
	<Chinese>China</Chinese>
</root>

输出的结果为:

[start element] ,root,root

	
[start element] ,head,head
[end element] ,head,head

	
[start element] ,body,body
world
[end element] ,body,body
out_of_tag_after_body

	
[start element] ,Chinese,Chinese
China
[end element] ,Chinese,Chinese


[end element] ,root,root

可以看出,
out_of_tag_after_body 在元素body 和Chinese之间,它也能够被Characters方法读出来,也就是说,Characters方法读的是tag之外的所有字符数据,
比如<a/><b/> 元素a,b之间即使没有数据,Characters也回去读它,只不过是空字符串而已。

Sax2的应用示例

下面是这个demo的全部代码:

import java.io.FileReader;

import org.xml.sax.XMLReader;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.helpers.DefaultHandler;


public class MySAXApp extends DefaultHandler
{

    public static void main (String args[])
	throws Exception
    {
	XMLReader xr = XMLReaderFactory.createXMLReader();
	MySAXApp handler = new MySAXApp();
	xr.setContentHandler(handler);
	xr.setErrorHandler(handler);

				// Parse each file provided on the
				// command line.
	for (int i = 0; i < args.length; i++) {
	    FileReader r = new FileReader(args[i]);
	    xr.parse(new InputSource(r));
	}
    }


    public MySAXApp ()
    {
	super();
    }


    
    // Event handlers.
    


    public void startDocument ()
    {
	System.out.println("Start document");
    }


    public void endDocument ()
    {
	System.out.println("End document");
    }


    public void startElement (String uri, String name,
			      String qName, Attributes atts)
    {
	if ("".equals (uri))
	    System.out.println("Start element: " + qName);
	else
	    System.out.println("Start element: {" + uri + "}" + name);
    }


    public void endElement (String uri, String name, String qName)
    {
	if ("".equals (uri))
	    System.out.println("End element: " + qName);
	else
	    System.out.println("End element:   {" + uri + "}" + name);
    }


    public void characters (char ch[], int start, int length)
    {
	System.out.print("Characters:    \"");
	for (int i = start; i < start + length; i++) {
	    switch (ch[i]) {
	    case '\\':
		System.out.print("\\\\");
		break;
	    case '"':
		System.out.print("\\\"");
		break;
	    case '\n':
		System.out.print("\\n");
		break;
	    case '\r':
		System.out.print("\\r");
		break;
	    case '\t':
		System.out.print("\\t");
		break;
	    default:
		System.out.print(ch[i]);
		break;
	    }
	}
	System.out.print("\"\n");
    }

}
输出

用到了如下的XML:

?xml version="1.0"?>

<poem xmlns="http://www.megginson.com/ns/exp/poetry">
<title>Roses are Red</title>
<l>Roses are red,</l>
<l>Violets are blue;</l>
<l>Sugar is sweet,</l>
<l>And I love you.</l>
</poem>

如果这个文档的名字叫做roses.xml并且在你的classpath中有一个叫 com.example.xml.SAXDriver(这个driver实际上并不存在)的SAX2 driver,你可以通过如下的方式去 

运行它:

java -Dorg.xml.sax.driver=com.example.xml.SAXDriver MySAXApp roses.xml
输出结果如下:

Start document
Start element: {http://www.megginson.com/ns/exp/poetry}poem
Characters:    "\n"
Start element: {http://www.megginson.com/ns/exp/poetry}title
Characters:    "Roses are Red"
End element:   {http://www.megginson.com/ns/exp/poetry}title
Characters:    "\n"
Start element: {http://www.megginson.com/ns/exp/poetry}l
Characters:    "Roses are red,"
End element:   {http://www.megginson.com/ns/exp/poetry}l
Characters:    "\n"
Start element: {http://www.megginson.com/ns/exp/poetry}l
Characters:    "Violets are blue;"
End element:   {http://www.megginson.com/ns/exp/poetry}l
Characters:    "\n"
Start element: {http://www.megginson.com/ns/exp/poetry}l
Characters:    "Sugar is sweet,"
End element:   {http://www.megginson.com/ns/exp/poetry}l
Characters:    "\n"
Start element: {http://www.megginson.com/ns/exp/poetry}l
Characters:    "And I love you."
End element:   {http://www.megginson.com/ns/exp/poetry}l
Characters:    "\n"
End element:   {http://www.megginson.com/ns/exp/poetry}poem
End document

如果这个roses.xml没有包含  xmlns="http://www.megginson.com/ns/exp/poetry" 这个名空间属性去申明所有的元素将都在这个名空间下,那么,就会有以下形式的输出:

Start document
Start element: poem
Characters:    "\n"
Start element: title
Characters:    "Roses are Red"
End element:   title
Characters:    "\n"
Start element: l
Characters:    "Roses are red,"
End element:   l
Characters:    "\n"
Start element: l
Characters:    "Violets are blue;"
End element:   l
Characters:    "\n"
Start element: l
Characters:    "Sugar is sweet,"
End element:   l
Characters:    "\n"
Start element: l
Characters:    "And I love you."
End element:   l
Characters:    "\n"
End element:   poem
End document

你可能用到这两种类型的document:一种用了namespace,另一种没有。对于element(或attributes),也存在同样的情况。因此,要具体情况

具体分析以便写出合适的code。

Element 的Attributes

element的attributes也可以有namespace,用法和element的一样。下面是一个demo:

XML:

<?xml version="1.0" encoding="UTF-8" ?>
<root xmlns="http://www.desmond.com" xmlns:attr="https://www.presley.com">
	<head hd="hd" isHead="true"/>
	<body attr:bd="bd">world</body>
	<Chinese cn="cn" zone="8">China</Chinese>
</root>

Code:

package com.aug.desmond.sax.start.test;

import java.io.FileReader;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

import static com.aug.desmond.util.Print.*;

public class MySAXApp extends DefaultHandler {
	public static void main(String[] args) throws Exception{
		XMLReader xr = XMLReaderFactory.createXMLReader();
		MySAXApp handler = new MySAXApp();
		xr.setContentHandler(handler);
		xr.setErrorHandler(handler);
		
		for(int i=0; i<args.length; i++) {
			FileReader r = new FileReader(args[i]);
			xr.parse(new InputSource(r));
		}
	}
	
	@Override
	public void startElement(String uri, String localName,
			String qName, Attributes attributes) throws SAXException {
		print("[element] uri="+uri+", localName="+localName);
		print("length of attributes=" + attributes.getLength());
		
		// get info of each attribute
		for(int i = 0; i < attributes.getLength(); i++) {
			System.out.print("[uri="+ attributes.getURI(i)
							+ ",qName=" + attributes.getQName(i) 
							+ ",localName=" + attributes.getLocalName(i) 
							+ ", type=" + attributes.getType(i)
							+ ",value=" + attributes.getValue(i) + "]");
		}
		
		// get attribute by qName
		print("zone="+attributes.getValue("zone"));
		print("bd="+attributes.getValue("attr:bd"));
		
		// get attribute by uri, localName
		print("bd="+attributes.getValue("https://www.presley.com", "bd"));
		print("*************************");
	}

	public MySAXApp() {
		super();
	}
}

Output:

[element] uri=http://www.desmond.com, localName=root
length of attributes=0
zone=null
bd=null
bd=null
*************************
[element] uri=http://www.desmond.com, localName=head
length of attributes=2
[uri=,qName=hd,localName=hd, type=CDATA,value=hd][uri=,qName=isHead,localName=isHead, type=CDATA,value=true]zone=null
bd=null
bd=null
*************************
[element] uri=http://www.desmond.com, localName=body
length of attributes=1
[uri=https://www.presley.com,qName=attr:bd,localName=bd, type=CDATA,value=bd]zone=null
bd=bd
bd=bd
*************************
[element] uri=http://www.desmond.com, localName=Chinese
length of attributes=2
[uri=,qName=cn,localName=cn, type=CDATA,value=cn][uri=,qName=zone,localName=zone, type=CDATA,value=8]zone=8
bd=null
bd=null
*************************

来自: http://www.saxproject.org/quickstart.html


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值