利用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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章