现在的位置: 首页 > 综合 > 正文

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

2014年01月31日 ⁄ 综合 ⁄ 共 5317字 ⁄ 字号 评论关闭

简介

当我们需要转换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

抱歉!评论已关闭.