java lucene技术(4):索引XML文档
——Xml解析工具:SAX——
Lucene的一大优点就是可以对多种数据格式建立索引,其中包括非纯文本格式的rich media 文档格式。虽然Lucene中没有包含能够自动索引非纯文本文档的工具;但我们可以使用免费的或商业的工具,从rich media文档中提取纯文本,然后依据上几章的方法对它们建立索引。
目前,XML文档是一种较为通用的文档格式,因此从本章开始,我将较详细的介绍处理XML格式文档的技术。
读取和操纵XML文件的标准方法是DOM(“文档对象模型”)。遗憾的是,这种方法需要读取整个文件并将它存储到树结构中,因而效率不高、缓慢,并且会过度使用资源。
一种替代方法是使用SimpleAPIforXML或SAX。SAX允许正在读取文档时处理该文档,这避免了在采取操作之前需要等待存储文档的所有内容。
SAX的API采用了事件驱动接口。在这个接口中,当某个分析事件发生时,解析器就会调用某个方法予以响应,这些方法包括文档或文档元素开始(如方法:startDocument(),startElement())、结束(如方法:endElement())、解析出错等等。
程序4.1向你展示了SAX是如何解析XML文档的。
class SAXXML extends DefaultHandler
{
private StringBuffer iobuf;
public void startDocument() throws SAXException
{
iobuf=new StringBuffer();
System.out.println("文档解析开始");
}
public void endDocument() throws SAXException
{
System.out.println("文档解析结");
}
public void startElement(String namespaceURI,String localName,String qName,Attributes atts)
{
iobuf.setLength(0);
System.out.println("标记名->"+qName);
for(int i=0;i<atts.getLength();i++)
{
System.out.println("属性值->"+atts.getValue(i));
}
}
public void endElement(String namespaceURI,String localName,String fullName )throws SAXException
{
System.out.println("文本值->"+iobuf.toString());
}
public void characters( char[] chars, int start, int length )throws SAXException
{
iobuf.append(chars,start,length);
}
public static void main(String args[])
{
try{
SAXParserFactory sf = SAXParserFactory.newInstance();
SAXParser sp = sf.newSAXParser();
SAXXML saxxml=new SAXXML();
sp.parse(new InputSource("E:/Test/xmldoc/tel.xml"),saxxml);
}catch(IOException e)
{
e.printStackTrace();
}catch(SAXException e)
{
e.printStackTrace();
}catch(Exception e)
{
e.printStackTrace();
}
}
}
其中,tel.xml内容如下:
<?xml version=’1.0′ encoding=’utf-8′?>
<personal-info>
<contact type="individual">
<name>wp</name>
<address>guomao</address>
<city>peking</city>
<province>anhui</province>
<postalcode>100086</postalcode>
<country>china</country>
<telephone>1352200</telephone>
</contact>
</personal-info>
通过测试结果,可以发现SAX的确可以很好的解析XML,将文档的元素标记和文本内容提取出来,而这就为下一步建立索引打下了基础。
既然,我们已经提取出XML中的内容,那么,我们就可以通过前面所介绍的建立索引方法分别对这些内容建立索引了。现在我们对程序4.1做简单的修改,实现索引功能:
程序4.2就是修改过的程序:
class SAXXML extends DefaultHandler
{
private StringBuffer iobuf = new StringBuffer();
private HashMap attrmap;
private Document doc;
public void startDocument() throws SAXException
{
doc = new Document();
System.out.println("文档解析开始");
}
public void startElement(String namespaceURI,String localName,String qName,Attributes atts)
{
iobuf.setLength(0);
if(atts.getLength()>0){
attrmap = new HashMap();
for(int i = 0; i < atts.getLength();i++)
attrmap.put(atts.getQName(i), atts.getValue(i));
}
}
public void characters( char[] chars, int start, int length )throws SAXException
{
iobuf.append(chars,start,length);
}
public void endElement(String namespaceURI,String localName,String fullName )throws SAXException
{
if (fullName.equals("personal-info")) {
return;
}
else if (fullName.equals("contact")) {
Iterator iter = attrmap.keySet().iterator();
while (iter.hasNext()) {
String attName = (String) iter.next();//System.out.println("—"+attName);
String attValue = (String) attrmap.get(attName);
doc.add(Field.Keyword(attName, attValue));
}
}
else {
doc.add(Field.Keyword(fullName, iobuf.toString()));
}
}
public Document getDoc(){
try{
SAXParserFactory sf = SAXParserFactory.newInstance();
SAXParser sp = sf.newSAXParser();
sp.parse(new InputSource("E:/Test/xmldoc/tel.xml"),this);
}catch(IOException e)
{
e.printStackTrace();
}catch(SAXException e)
{
e.printStackTrace();
}catch(Exception e)
{
e.printStackTrace();
}
return doc;
}
public void endDocument() throws SAXException
{
System.out.println("文档解析结束");
}
public static void main(String args[])
{
SAXXML SXml = new SAXXML();
Document document = SXml.getDoc();
System.out.println(document);
}
}
经过测试,控制台显示的索引Doucment为:
Document<Keyword<name:wp>Keyword<address:guomao> Keyword<city:peking>Keyword<province:anhui> Keyword<postalcode:100086>Keyword<country:china> Keyword<telephone:1352200> Keyword<type:individual>>
既然我们已经得到了索引Doucment,下一步的工作就很简单了。
建立方法buildIndex();
publicvoid buildIndex(Document document) throws IOException{
File file = new File("E:/Test/indexroom");
IndexWriter writer = new IndexWriter(file,newSimpleAnalyzer(),true);
writer.addDocument(document);
writer.close();
}
到此,我们已经实现了一个完整的对XML文档索引的过程。
SAX接受XML文档,在读入XML文档的过程中就进行解析,也就是说读入文档的过程和解析的过程是同时进行的,这和DOM区别很大。解析开始之前,需要向XMLReader注册一个ContentHandler,在ContentHandler中定义了很多方法,比如startDocument(),它定制了当在解析过程中,遇到文档开始时应该处理的事情。当XMLReader读到合适的内容,就会抛出相应的事件,并把这个事件的处理权代理给ContentHandler,调用其相应的方法进行响应。
从一定角度来说,SAX的确是个很好轻量型解决方法,感兴趣的读者可以根据自己项目的实际,依据SAX的思想,提出更好的解决方法。
唯有斯人面上簌簌流下的,是点点无声无行的热泪。