利用SAX和XSLT转换Flat Files为XML格式

简介

当我们需要转换XML文件到其它格式文件的时候,XSLT (eXtensible Stylesheet Language for Transformations)是一个很好的选择。但是,有的时候我们需要将一个flat文件或者非XML数据结构转换为XML和其他标记性语言,如果我们能使用XSLT来转换以上的数据结构,那绝对是一件很爽的事情。

问题的答案是可以,我们可以使用SAX来 (Simple API for XML)做这件事情。本文将介绍如何创建一个JAVA类,通过该类转换JAVA的属性文件(*.properties)为XML格式。示例将完全证明这个观点,并且帮助你学习转换任何数据结构到XML文件的技术。

本文分为以下几个部分:

  • SAX Parser和Handler Review
  • 创建一个自定义的SAX Parser (it's easier than you think)
  • The "Echo" Stylesheet
  • 使用TrAX (Transformation API for XML) 转换SAX Source
    总结


SAX Parser和Handler Review

如果你之前了解SAX,你应该知道SAX是将XML文档作为事件流处理的API,你可能写过handler类来接收这些事件。handler类会在下列这些事件触发时候得到通知:

  • Start of Document
  • Start of Element
  • Characters
  • End of Element
  • End of Document

handler类会如你所愿响应这些事件,最容易的方法是通过继承DefaultHandler对象实现ContentHandler接口。

使用一个自定义的handler解析一个XML文件,可能需要以下代码: 

File f  =   new  File( " test.xml " );
ContentHandler handler 
=   new  YourCustomHandler();

SAXParserFactory factory 
=  SAXParserFactory.newInstance();
SAXParser parser 
=  factory.newSAXParser();
parser.parse(file, handler);

 

SAXParser将执行YourCustomHandler中的回调函数。


创建一个自定义的SAX Parser

在与非XML数据结构打交道的时候,我们需要创建一个Parser用来对已注册的handler类进行SAX事件广播。我们甚至可以不写一个handler类,如果你习惯于动手写handler的话,这样看上去很奇怪。

SAXSource对象,用于表示用于转换的输入内容,需要用到一个关联TrAX API的parser。SAXSource对象创建时候以一个实现XMLReader接口的对象作为参数。这个接口包括几个方法,大多数在我们的例子中还用不到。

下面我们将创建一个基于XMLReader接口的实现,用于转换JAVA属性文件为一个XML事件流。该例子虽然简单,但已经足够证明可以将任意数据结构转换为XML格式。

用于转换的属性文件如下所示: 

Font-Family=Arial
Font-Size=12pt
Background-Color=White
Foreground-Color=Black

 

我们看到该文件数据结构实现上是由若干个key value pairs组成,现在我们需要读取该文件,并转换为一系列的SAX事件:

public   class  PropertyFileParser  implements  XMLReader
{
private  ContentHandler contentHandler  =   null ;

  
public  ContentHandler getContentHandler()
  {
    
return  contentHandler;
  }
  
public   void  setContentHandler(ContentHandler handler)
  {
    contentHandler 
=  handler;
  }
}


PropertyFileParser实现了XMLReader接口,尽管我们不是必须得自己写一个ContentHandler,但我们必须要为content handlers提供一种机制,用于从parser注册并接收事件。本节中TrAX 将为我们提供这样一个content handler。


我们的主要任务是实现parse() 方法,这是XMLReader接口所要求实现的。这里我们取得InputSource,并通过其载入Properties对象。然后,我们调用我们自定义的parse方法。

public   void  parse(InputSource source)  throws  IOException,
                                             SAXException
{
  InputStream is 
=  source.getByteStream();
  Properties p 
=   new  Properties();
  p.load(is);
  parse(p);
}


自定义的parse方法首先针对文档的根元素利用startDocument()和startElement()事件进行广播。通过迭代器遍历properties组成的enumeration,为每个属性生成startElement(), characters(), endElement()事件。最后,根节点的endElement() 和endDocument()被调用。

private   void  parse(Properties p)  throws  SAXException
{
  contentHandler.startDocument();
  contentHandler.startElement(namespaceURI,
                              
" Properties " ,
                              
" Properties " , attribs);

  Enumeration e 
=  p.propertyNames();

while  (e.hasMoreElements())
{
  String key 
=  (String)e.nextElement();
  String value 
=  (String)p.getProperty(key);

    contentHandler.startElement(namespaceURI, key, key, attribs);
    contentHandler.characters(value.toCharArray(), 
0 ,
                              value.length());
    contentHandler.endElement(namespaceURI, key, key);
}

  contentHandler.endElement(namespaceURI, 
" Properties " ,
                                          
" Properties " );
  contentHandler.endDocument();
}


为满足XMLReader接口的要求,我们通过空方法来实现其他几个接口方法,对于我们的SAX Parser来说自然是小菜一碟。完整的类请参见:PropertyFileParser.java

Echo Stylesheet

接下来,我们将使用一个非常简单的Stylesheet对我们parser所关联的XML文档进行输出。该Stylesheet的文件名为echo.xsl,起这个名字的原因是我们对输入文档所作的转换很简单,也就是echo一下。到了这一步剩下的事情就非常简单了,和对XML文件的格式化输出几乎没有什么区别。在这里stylesheet用一种间接的方式扮演了handler的角色,或者说是parser进行事件广播的接收者。

<? xml version="1.0" encoding="UTF-8" ?>

< xsl:stylesheet  version ="1.0"
                xmlns:xsl
="http://www.w3.org/1999/XSL/Transform"
                xmlns:fo
="http://www.w3.org/1999/XSL/Format" >

  
< xsl:output  method ="xml"  indent ="yes" />

  
< xsl:template  match ="node()" >
    
< xsl:copy >
      
< xsl:apply-templates />
    
</ xsl:copy >
  
</ xsl:template >

</ xsl:stylesheet >


我们的"echo" stylesheet完成了一项"deep copy",这个单一的模版针对所有的文档节点,它复制上下文节点并且所有的子节点都执行了<xsl:apply-templates/>.。这种类型的模版针对XML文档进行全局性的修改是很有用的。

以下的代码并没有进行效率上的优化,如果有此需要请参考作者的另外一篇文章:Optimizing Stylesheet Execution With The Transformation API for XML。 

public   static   void  main(String[] args)  throws  Exception
{
//  construct SAXSource with our custom XMLReader
InputStream props  =  ClassLoader.getSystemResourceAsStream
                    (
" my.properties " );
InputSource inputSource 
=   new  InputSource(props);
XMLReader parser 
=   new  PropertyFileParser();
SAXSource saxSource 
=   new  SAXSource(parser, inputSource);

//  construct a transformer using the echo stylesheet
TransformerFactory factory  =  TransformerFactory.newInstance();
StreamSource xslSource 
=   new  StreamSource( " echo.xsl " );
Transformer transformer 
=  factory.newTransformer(xslSource);

//  transform the SAXSource to the result
StreamResult result  =   new  StreamResult( " properties.xml " );
transformer.transform(saxSource, result);
}

 

使用了TrAX API,main()方法完成了如下几个步骤:

SAXSource被创建,包括两个参数:PropertyFileParser以及input source(保存了需要解析的属性文件)。
利用"echo" stylesheet创建了transformer对象。
SAXSource被转换为一个Result对象。

虽然示例中我们为了输出文件使用了StreamResult,输出的结果也可以是DOM或者其他类型的Result对象。以下是转换后的结果: 

<? xml version="1.0" encoding="UTF-8" ?>
< Properties >
  
< Background-Color > White </ Background-Color >
  
< Font-Family > Arial </ Font-Family >
  
< Font-Size > 12pt </ Font-Size >
  
< Foreground-Color > Black </ Foreground-Color > >
</ Properties >

 

总结:

转换非XML数据结构到XML是一个很普遍的问题。大多数的自定义方法都比较麻烦,步骤较多。我们通过示例证明使用标准的XML APIs来完成这项工作是完全可行的。我们的方法可以减少非必要的步骤,直接从源格式转化到目标XML格式。通过学习和举一反三,我们很容易发挥SAX和XSLT的效力,实现任意数据结构到XML格式的转换。 

示例源码下载

原文作者:Jeff Ryan

原文出处:Transforming Flat Files To XML With SAX and XSLT

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页