引言:
Smooks是一個開源的Java框架,用於處理“數據事件流”。它常常被認為是一個轉換框架並以此被用於好幾個產品和項目中,包括JBoss ESB(以及其它ESB)。然而究其核心,Smooks未提及“轉換”或者其它相關的詞匯。它的應用遠不只這一點!
Smooks 是一款基於 LGPL 協議的開源 Java 框架,主要用於處理 XML 與 non-XML 格式 ( 包括 CSV,EDI,Java) 之間的轉換。Smooks 於 2008 年 5 月發布 v1.0,目前最新的版本已是 v1.3.1。正如 Smooks 開發人員所言,“Smooks …讓操作 XML, EDI,XML,CSV 變得更簡單”。
實際上,我們通常將 Smooks 看做是一個 XML 到 non-XML 格式的“數據轉換引擎”,然而在 Smooks 的核心實現中,本質上是為 XML, CSV, EDI 和 Java 等數據源提供了一個事件流(Event Stream),對數據源的處理其實可以看做是針對此輸入數據流的一個個邏輯事件處理。因此,Smooks 的核心其實是一個“結構化數據事件流處理器”。
Smooks:
- 多源數據格式:“輕易”地消費諸多流行的數據格式,包括XML,EDI,CSV,JSON和Java(是的,你可以以一種聲明性的方式完成java到java的轉換)。
- 轉換:利用諸多選項消費由數據源產生的事件流,產生其它格式的結果(即,對源進行“轉換”)。這包括能在過濾源數據流時對Smooks所捕獲的數據模型應用FreeMarker和XSL模板。鑒於這些模板資源能被運用於源數據事件流內部的事件,因此它們能被用來執行“基於片斷的轉換(fragment based transforms)”。這意味着Smooks能被用於對數據源的片斷執行轉換,而不僅限於將數據源作為一個整體來施行轉換。
- Java綁定:以一種標准方式由所支持的數據格式(即不僅限於XML)創建和生成Java對象模型/圖。由EDI數據源 生成某對象模型的配置與由XML數據源或JSON數據源生成對象模型所進行的配置幾乎一模一樣。唯一區別在於綁定配置的“事件選擇器(event selector)”取值不同。
- 大型消息處理:Smooks支持多種處理大型消息(GBs)的方式,它是通過基於SAX的過濾器完成的。由於綜合了 基於片斷轉換、Java綁定,以及使用節點模型混合DOM和SAX模型所帶來的能力,Smooks可以使用較低的內存空間處理大型消息。這些能力允許你執 行直接的1對1轉換,同時也支持對大型消息數據流執行1對多的分解、轉換和路由。
- 消息修飾:使用數據庫數據修飾消息。這可以按片斷來完成,即你可以按片斷來管理在一個片斷上的修飾。與此相關的一個用例是一個包含了消費者ID列表的消息在發往另一個流程前需要從數據庫提取消費者地址和概要數據來豐富。
- 基於可擴展XSD的配置模型:從Smooks v1.1開始,你可以用自己的可重用定制訪問者邏輯配置模型來擴展Smooks XSD配置名字空間。創建這些定制配置擴展只是一項簡單的配置工作,這個工作極大的增進了這些重用組件的可用性。所有的現有Smooks預置組件都利用了這一工具。
Java-to-XML實例:
工具:Eclipse,Smooks
這里采用的是Smooks自帶的java-to-xml實例(example文件夾下):
首先,需要做的是在Eclipse中安裝Smooks插件。
- 打開Eclipse,點擊【Help】-【Install New SoftWare...】-【add】:
Name只可以隨意設置,Location設置訪問【http://download.jboss.org/jbosstools/updates/】,自己選擇要用的版本,我這里使用的是【http://download.jboss.org/jbosstools/updates/JBossTools-3.1.1.GA/】
- 設置成功后,選擇【Data Services】-【Smooks Tools】,點擊【next】,完成安裝

然后,編寫.xml文件,新建xml源文件夾
Orders.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <order> 3 <header> 4 <date>Wed Nov 15 13:45:28</date> 5 <customer number = "123321">Fx</customer> 6 </header> 7 <order-items> 8 <order-item> 9 <product>111</product> 10 <quantity>2</quantity> 11 <price>100</price> 12 </order-item> 13 <order-item> 14 <product>222</product> 15 <quantity>3</quantity> 16 <price>200</price> 17 </order-item> 18 </order-items> 19 </order>
接下里,編寫目標Java文件:
Header.java
1 package com.fx.java; 2 3 public class Header { 4 private String date; 5 private String customerNumber; 6 private String customerName; 7 8 public Header(){ 9 date = null; 10 customerNumber = null; 11 customerName = null; 12 } 13 14 public Header(String data, String customerNumber, String customerName) { 15 this.date = data; 16 this.customerNumber = customerNumber; 17 this.customerName = customerName; 18 } 19 20 public String getDate() { 21 return date; 22 } 23 24 public void setDate(String date) { 25 this.date = date; 26 } 27 28 public String getCustomerNumber() { 29 return customerNumber; 30 } 31 32 public void setCustomerNumber(String customerNumber) { 33 this.customerNumber = customerNumber; 34 } 35 36 public String getCustomerName() { 37 return customerName; 38 } 39 40 public void setCustomerName(String customerName) { 41 this.customerName = customerName; 42 } 43 44 }
OrderItem.java
1 package com.fx.java; 2 3 public class OrderItem { 4 5 private String productId; 6 private String quantity; 7 private String price; 8 9 public OrderItem() { 10 productId = null; 11 quantity = null; 12 price = null; 13 } 14 15 public OrderItem(String productId, String quantity, String price) { 16 this.productId = productId; 17 this.quantity = quantity; 18 this.price = price; 19 } 20 21 public String getProductId() { 22 return productId; 23 } 24 25 public void setProductId(String productId) { 26 this.productId = productId; 27 } 28 29 public String getQuantity() { 30 return quantity; 31 } 32 33 public void setQuantity(String quantity) { 34 this.quantity = quantity; 35 } 36 37 public String getPrice() { 38 return price; 39 } 40 41 public void setPrice(String price) { 42 this.price = price; 43 } 44 45 }
Order.java
1 package com.fx.java; 2 3 import java.util.Vector; 4 5 public class Order { 6 public Header header; 7 public Vector<OrderItem> orderItems; 8 9 public Order(){ 10 header = new Header(); 11 orderItems = new Vector<OrderItem>(); 12 } 13 14 public void setHeader(Header header) { 15 this.header = header; 16 } 17 18 public void setOrderItems(OrderItem orderItems) { 19 this.orderItems.add(orderItems); 20 } 21 22 }
接下來,進行作重要的一步:配置Smooks完成xml到 Java對象的映射:
新建一個 Smooks 配置文件:File > New > Smooks > Smooks Configuration File,選擇剛才創建的 Java Project 的 src 文件夾作為 Smooks 配置文件的父目錄,保留缺省的文件名 smooks-config.xml,單擊 Finish 生成初始的配置文件。此時在 Smooks Processing 編輯器中只有一個 Input Task 圖標。

單擊 Input Task 圖標,則可以看到 Smooks input reader 的屬性配置界面。由於我們希望完成從 XML 到 Java 的綁定,因此在 Input Type 的下拉菜單中選擇“Input Type”為 XML。接下來,在配置界面右側的 Input Data 中單擊 Add 將源 XML 文件添加到項目中。Smooks 會自動解析此 XML 文件並在 Input Model View 中列出其數據模型。

在完成了源數據導入之后,我們要開始配置 Java 綁定。將鼠標移至 Input Task 的藍色箭頭右側,將會有一個綠色加號(+)出現,單擊此加號並選擇出現的提示“Java 映射;為了將開發的 Java 類導入 Smooks 配置,在 Selected Task Details 配置框中空白處單擊鼠標右鍵,選擇 Add > Java Class,將上文中開發的 Header,OrderItem 以及 Order 類導入。然后就可以非常方便的使用拖曳的方式在左邊的 XML 數據模型和右邊的 Java 類和成員變量之間建立映射關系,最后的結果如下圖所示:

xml表現形式為:
smooks-config.xml
1 <?xml version="1.0" encoding="ASCII"?><smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:jb="http://www.milyn.org/xsd/smooks/javabean-1.2.xsd"> 2 <params> 3 <param name="stream.filter.type">SAX</param> 4 <param name="inputType">input.xml</param> 5 <param name="input.xml" type="input.type.actived">Workspace://smooks xml-to-java/xml/Orders.xml</param> 6 </params> 7 <jb:bean beanId="Header" class="com.fx.java.Header" createOnElement="/order/header"> 8 <jb:value data="/order/header/customer" property="customerName"/> 9 <jb:value data="/order/header/customer/@number" property="customerNumber"/> 10 <jb:value data="/order/header/date" property="date"/> 11 </jb:bean> 12 <jb:bean beanId="OrderItem" class="com.fx.java.OrderItem" createOnElement="/order/order-items/order-item"> 13 <jb:value data="/order/order-items/order-item/price" property="price"/> 14 <jb:value data="/order/order-items/order-item/product" property="productId"/> 15 <jb:value data="/order/order-items/order-item/quantity" property="quantity"/> 16 </jb:bean> 17 <jb:bean beanId="Order" class="com.fx.java.Order" createOnElement="order"/> 18 </smooks-resource-list>
最后,進行測試(要記得導入Smooks所需要的架包,將下載下來的Smooks版本的lib文件夾下的所有架包全部導進來):
TestSmooks.java
1 package com.fx.test; 2 3 import java.io.FileInputStream; 4 import java.io.IOException; 5 import javax.xml.transform.stream.StreamSource; 6 import org.milyn.Smooks; 7 import org.milyn.payload.JavaResult; 8 import org.xml.sax.SAXException; 9 import com.fx.java.Header; 10 import com.fx.java.OrderItem; 11 12 public class TestSmooks { 13 14 public static void main(String[] args) throws IOException, SAXException { 15 Smooks smooks = new Smooks("smooks-config.xml"); 16 JavaResult javaResult = new JavaResult(); 17 18 smooks.filterSource(new StreamSource(new FileInputStream("F:\\Users\\FANXUAN\\JunoWorkSpace\\smooks xml-to-java\\xml\\Orders.xml")), javaResult); 19 Header header = (Header)javaResult.getBean("Header"); 20 OrderItem orderItem = (OrderItem)javaResult.getBean("OrderItem"); 21 22 System.out.println("--->" + header.getCustomerNumber()); 23 System.out.println("--->" + header.getCustomerName()); 24 System.out.println(); 25 System.out.println("--->" + orderItem.getProductId()); 26 System.out.println("--->" + orderItem.getQuantity()); 27 System.out.println("--->" + orderItem.getPrice()); 28 } 29 30 31 }
運行得到結果:

由此,我們完成了xml到java轉換的配置和測試!
