sax数据离散化
注意: 本技巧使用JAXP。 这些类也是Java 2 SDK 1.4的一部分,因此,如果您安装了1.4,则不需要任何其他软件。 您可以下载本文的源文件(请参阅参考资料 )。
SAX解析器如何工作
XML的简单API(SAX)是基于事件的API。 它逐字符检查XML文件,并将其转换为一系列事件,例如startDocument()
和endElement()
。 ContentHandler
对象将处理这些事件,并采取适当的措施。 ErrorHandler
对象负责解析过程中出现的任何警告或错误。 主应用程序(请参见清单1)将这些对象分配给XMLReader
对象:
parse()
方法只是将事件发送到content
对象,然后由content
对象处理。
清单1.主要应用程序
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.XMLReader;
import org.xml.sax.SAXException;
import org.xml.sax.InputSource;
import java.io.IOException; public class MainSaxApp
{ public static void main (String[] args)
{ try
{ String parserClass = "org.apache.crimson.parser.XMLReaderImpl";
XMLReader reader = XMLReaderFactory.createXMLReader(parserClass);
WeblogHandler content = new WeblogHandler();
ErrorProcessor errors = new ErrorProcessor();
reader.setContentHandler(content); reader.setErrorHandler(errors);
InputSource file = new InputSource("changes.xml"); reader.parse(file); }
catch (IOException ioe) { System.out.println("IO Exception: "+ioe.getMessage()); } catch (SAXException se)
{ System.out.println(se.getMessage());
}
}
}
处理程序
对于此应用程序,所有工作将由WeblogHandler
对象完成,该对象处理XML文件。 changes.xml文件本身非常简单,所有实际数据都包含在属性中:
清单2.数据文件的一部分
<?xml version="1.0"?>
<weblogUpdates version="1"
updated="Sat, 15 Jun 2002 22:25:06 GMT"
count="592697">
<weblog name="Enigmatic Mermaid"
url="http://pombostrans.blogspot.com"
when="28"/>
<weblog name="The Vanguard Science Fiction Report"
url="http://www.vanguardreport.com" when="852"/>
<weblog name="Flummox.com"
url="http://www.flummox.com/" when="10713"/>
</weblogUpdates>
这只是实际文件的一小段,但它显示了结构:属性包括名称,URL和自Weblog更新以来的时间(以秒为单位)。 内容处理程序将其中的某些信息输出到窗口:
清单3.内容处理程序
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.Attributes;
public class WeblogHandler extends DefaultHandler
{
public WeblogHandler ()
{
super();
}
int numLogs = 0;
public void startElement (String namespaceUri, String localName,
String qualifiedName, Attributes attributes) {
if (localName.equals("weblog")) {
String logName = attributes.getValue("name");
String secsAgo = attributes.getValue("when");
numLogs = numLogs + 1;
System.out.println(numLogs + ") " + logName
+ " updated " + secsAgo + " seconds ago.");
}
}
public void endDocument(){
System.out.println();
System.out.println("All recorded logs displayed.");
System.out.println("More may have been updated within"
+ " the appropriate timeframe.");
}
}
在这种情况下,错误处理程序很简单,只是在出现错误或警告时提醒您。 源文件包括整个文件。
运行应用程序
当您实际运行MainSaxApp
,changes.xml中的所有数据都传递给content
,后者输出适当的信息,如图1所示。
图1.显示所有Weblog。
请注意,通过执行endDocument()
方法可以证明整个文件已被解析。
停止解析器
如您所见,在changes.xml跟踪的三个小时内,已经更新了大量的Weblog。 假设您要允许用户输入代表他或她感兴趣的时间间隔的秒数。 为此,您将在命令行上查看第一个参数,并将其传递给内容对象。 (稍后将查看对WeblogHandler.java
的相应更改。)
清单4.对MainSaxApp.java的更改
...
String parserClass = "org.apache.crimson.parser.XMLReaderImpl";
XMLReader reader = XMLReaderFactory.createXMLReader(parserClass);
WeblogHandler content = new WeblogHandler();
int numSecs = new Integer(args[0]).intValue();
content.setNumSecs(numSecs);
ErrorProcessor errors = new ErrorProcessor();
reader.setContentHandler(content);
reader.setErrorHandler(errors);
...
当然,除非更改WeblogHandler
类,否则这些更改将毫无意义:
清单5.对WeblogHandler.java的更改
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
public class WeblogHandler extends DefaultHandler
{
public WeblogHandler ()
{ super(); }
//-------------
//UTILITY METHODS
//-------------
int numSecs = 0;
public void setNumSecs(int arg) {
numSecs = arg;
}
//-------------
//EVENT METHODS
//-------------
int numLogs = 0;
public void startElement (String namespaceUri, String localName,
String qualifiedName, Attributes attributes)
throws SAXException {
if (localName.equals("weblog")) {
String logName = attributes.getValue("name");
String logURL = attributes.getValue("url");
int secsAgo = new Integer(attributes.getValue("when")).intValue();
if (secsAgo > numSecs) {
throw new SAXException("\nLimit reached after "+numLogs+" entries.");
} else {
numLogs = numLogs + 1;
System.out.println(numLogs + ") " + logName +
" updated " + secsAgo + " seconds ago.");
}
}
}
public void endDocument(){
System.out.println();
System.out.println("All recorded logs displayed.");
System.out.println("More may have been updated within"
+ " the appropriate timeframe.");
}
}
首先,为参数添加setNumSecs()
方法。 接下来,将when
属性检索为int
而不是String
。 幸运的是,changes.xml是基于when
属性排序的,因此您所要做的就是将当前的secsAgo
与numSecs
进行比较; 如果secsAgo
超过numSecs
, numSecs
停止解析。
为了停止解析,您抛出了一个新的SAXException
,并使用一条消息创建了该消息,该消息包括到目前为止已处理的日志数。 那么当您运行它时会发生什么呢?
运行新的应用程序
现在,如果使用一个新的参数(例如5分钟)运行新应用程序(例如,使用java MainSaxApp 300
),您将看到区别, 如图2所示。
图2.前五分钟。
那么,这里到底发生了什么? 您输入的参数为300秒,因此,当第一个Web日志的更新时间超过300秒时, startElement()
方法将引发SAXException
。 因为没有try-catch
块来捕获该异常,所以startElement()
会将其抛出到调用环境中,该环境是在MainSaxApp
调用的reader
的parse()
方法。 也没有什么可以捕获的,因此转到MainSaxApp
的main()
方法,在该方法中, try-catch
块输出传递的消息。
重点是:因为应用程序引发了异常,所以解析器停止了-正如从未执行过endDocument()
方法这一事实所证明-但您仍然拥有它已经遇到的所有信息。
下一步
本技巧演示了一个简单的应用程序,其中包括一个SAX解析器,该解析器在遇到特定条件时会停止。 在这里,您仅使用了泛型SAXException
,但是没有什么可以阻止您针对不同的业务条件创建自己的异常并将其使用纳入您的逻辑中。 (使用命令行参数时,您还想执行更多错误检查!)
翻译自: https://www.ibm.com/developerworks/xml/library/x-tipsaxstop/index.html
sax数据离散化