Kettle - 生成xml文件

Kettle 使用備忘錄 1- 生成xml文件

 (2012-06-12 10:36:36)
標籤: 

it

分類: ETL
最近被抓壯丁去弄一個ETL的項目,要求用開源的kettle去替代原來平臺上的datastage。
本文主要是記錄下在使用kettle過程中碰到的頭疼的問題和解決方案。。。

1. 利用 excel中的數據生成xml文件

kettle中的xml文件輸出組件的功能其實是很弱的,所以要生成較爲複雜的xml文件時需要使用組件:
add xml ,  xml join,  placeholder, js script 等等
如下是一個例子:
Kettle <wbr>使用備忘錄 <wbr>1- <wbr>生成xml文件
使用merge join主要是爲了減少在xml join中需要匹配的結果集合(因爲xml join中的匹配功能其實是很弱的)。
在merge join之前記錄必須先排序。
placeholder 組件是往記錄流中添加一個常量,這個常量在 add xml中通常不被設爲屬性,而是在xml join中用於放置需要被組裝進去的xml元素。
以生成如下簡單的xml文件爲例:
<?xml version="1.0" encoding="UTF-8"?>
<document>
    <section name="foo">
    <p>
        Hello from section foo
    </p>
    </section>
    <section name="bar">
    <p>
        Hello from section bar
    </p>
    </section>
</document>
對應的kettle 轉換爲:
Kettle <wbr>使用備忘錄 <wbr>1- <wbr>生成xml文件

首先需要從excel文件中讀取數據,excel文件中的內容爲
Kettle <wbr>使用備忘錄 <wbr>1- <wbr>生成xml文件
利用讀取到的內容生成xml元素,如下是add xml中的設置:
Kettle <wbr>使用備忘錄 <wbr>1- <wbr>生成xml文件

Kettle <wbr>使用備忘錄 <wbr>1- <wbr>生成xml文件
這一步主要生成了如下的xml元素:
<section name="foo">
    <p>
        Hello from section foo
    </p>
</section>
<section name="bar">
    <p>
        Hello from section bar
    </p>
  </section>
接下來要生成 <document>元素,因爲該元素沒有對應的數據,所以使用“生成記錄”組件生成一條空記錄用於與之前的xml元素進行xml join。它的功能有點類似與之前提到的placeholder,不同的是這個可以作爲起始輸入,而placeholder是一個轉換步驟。
Kettle <wbr>使用備忘錄 <wbr>1- <wbr>生成xml文件
然後是利用這條生成的記錄生成xml元素document,注意屬性選否:
Kettle <wbr>使用備忘錄 <wbr>1- <wbr>生成xml文件

Kettle <wbr>使用備忘錄 <wbr>1- <wbr>生成xml文件

接着就是進行xml join了,source stream中的xml元素會被拼接到target stream的xml元素中。怎麼拼接有join condition properties決定,它是使用xpath來定位要拼接的位置,例如//doc 就是把source stream的元素拼接到每個doc元素中。需要注意的是,由於這次xml join之後xml文件就生成好了,所以不能把omit xml header選上。
Kettle <wbr>使用備忘錄 <wbr>1- <wbr>生成xml文件
當然通過xpath也可以做複雜的條件join,如下是個例子:
Kettle <wbr>使用備忘錄 <wbr>1- <wbr>生成xml文件

可以通過xpath指定在所有target stream的area元素中如果area的屬性STATIONID的值與source stream中STATIONID的值(這個值不一定要在source的xml元素中,但必須在source stream中,例如可以是一開始從excel讀入的流中)相同,那麼把source stream中的xml元素放到 orderlinecomments元素中。

這步之後生成的xml文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<document>
 <doc>
    <section name="foo">
    <p>
        Hello from section foo
    </p>
    </section>
    <section name="bar">
    <p>
        Hello from section bar
    </p>
    </section>
 </doc>
</document>
和我們預期的多了<doc>和</doc>,這是我們使用js腳本進行替換:
Kettle <wbr>使用備忘錄 <wbr>1- <wbr>生成xml文件

js腳本處理好之後,輸出流的名字就換成 xmlOderLinesNew了。
最後把這個流輸出到文件中,需要注意的是在內容中不要使用“分割符”,“封閉符”,和“頭部”:
Kettle <wbr>使用備忘錄 <wbr>1- <wbr>生成xml文件

Kettle <wbr>使用備忘錄 <wbr>1- <wbr>生成xml文件

Kettle <wbr>使用備忘錄 <wbr>1- <wbr>生成xml文件


--關於kettle生成xml文件的性能問題
各個步驟中addxml的效率不錯,但是xmljoin在大數據量的情況下效率實在太低。。。,按照我的理解,kettle通過addxml生成一個xml內容的字符串,然後xmljoin其實是對兩個大字符串進行重構,這個重構過程還涉及到字符子串的定位,拷貝,比較,甚至是字符串子串級別的join。。。效率很難能高,所以在數據量比較大的時候應該避免使用xmljoin。
其實我們使用xmljoin主要是爲了生成層次化的xml文件。作爲替代方案,首先在使用addxml生成xml字符串之前,先使用merge join把目標xml文件中所需的數據先生成出來,這個時候生成的xml字符串中的xml是幾乎沒有層次的:
例如
<?xml version="1.0" encoding="ISO-8859-1"?>
<Users>
<User id="2" name="ABC" Division="HR" country="C"/>
<User id="3" name="xyz" Division="Admin" country="D"/>
<User id="4" name="LMN" Division="Admin" country="D"/>
<User id="5" name="PQR" Division="HR" country="C"/>
</Users>
然後我們可以通過xslt進行xml文件結構的重構,xsl文件的示例如下:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

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

    <xsl:key name="division" match="User" use="concat(@Division, ',', @country)" />

    <xsl:template match="Users">
        <AllUsers>
            <xsl:apply-templates select="User[generate-id()=generate-id(key('division',concat(@Division, ',', @country))[1])]"/>
        </AllUsers>
    </xsl:template>

    <xsl:template match="User">
        <Division value="{@Division}" country="{@country}">
            <xsl:for-each select="key('division', concat(@Division, ',', @country))">
                <User id="{@id}" name="{@name}"/>
            </xsl:for-each>
        </Division>
    </xsl:template>

</xsl:stylesheet>
生成的目標xml文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<AllUsers>
<Division country="C" value="HR">
<User name="ABC" id="2"/>
<User name="PQR" id="5"/>
</Division>
<Division country="D" value="Admin">
<User name="xyz" id="3"/>
</Division>
<Division country="D" value="Payroll">
<User name="LMN" id="4"/>
</Division>
</AllUsers>

具體到kettle中我們可以使用xsl transformation組件來進行轉換:
Kettle <wbr>使用備忘錄 <wbr>1- <wbr>生成xml文件
其中的xsl 轉換:
Kettle <wbr>使用備忘錄 <wbr>1- <wbr>生成xml文件

理想的狀態是我們直接使用轉換中的XSL Transformation組件,在轉換級別就解決問題,但事與願違,這個步驟老是報錯。。。Kettle <wbr>使用備忘錄 <wbr>1- <wbr>生成xml文件
只好使用job級別的XSL Transformation組件,這樣會比較麻煩,要先通過一個轉換生成中間xml文件保存到磁盤,然後在調用XSL Transformation組件讀取這個中間xml文件以及定義好的xsl文件生成目標xml文件。。
Kettle <wbr>使用備忘錄 <wbr>1- <wbr>生成xml文件

還有一種思路是不使用kettle的xsl transformation組件,而是直接在kettle的User Define Java Class組件中通過java代碼調用xsl文件進行轉換,java代碼調用xsl文件和xml文件進行轉換的代碼可以參考:
http://www.blogjava.net/yangxiang/archive/2009/08/11/290688.html

關於xsl文件的編寫可以參考
http://stackoverflow.com/questions/2146648/how-to-apply-group-by-on-xslt-elements
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章