使用DOM和XSL來格式化由Java提取的資料
最後更新:2017-02-28
來源:互聯網
上載者:User
dom|資料 Java可從任何JDBC相容資料庫提取資料,將資料轉換成一個DOM對象,然後用XSL將資料格式化成需要的形式。在上一篇文章中,我們示範了如何從資料庫中程式化地提取資料。現在,讓我們討論如何產生DOM對象,並用一個XSL樣式表來格式化資料。這樣一來,最終的輸出就可用於任何應用程式,只要你為它們提供需要的輸入。
產生DOM文檔
Java的最新版本支援JAXP XML處理,並實現了由全球資訊網協會(W3C)定義的DOM API。使用這些相容於JAXP的版本,只需3行代碼即可建立一個DOM文檔對象,其中用到了JAXP factory和builder方法:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.newDocument();
我們建立一個名為resultset的根項目,並把它添加到文檔對象中
Element root = document.createElement("resultset");
document.appendChild(root);
resultset遊標遍曆結果集時,在根項目中添加包含行資料的一個新元素:
Element nextRow = document.createElement("row");
列計數必須從ResultSetMetaData對象中讀取;在本項目的資料庫資料提取階段,它必須是已經定義好的:
int columnCount = rsmd.getColumnCount();
列名要用for迴圈來檢索;針對每個列名,都添加一個name節點,它帶有一個子TextNode,其中包含用於一個names元素的名值:
String[] columnNames = new String[columnCount];
Element names = document.createElement("names");
for (int i = 0; i 〈 columnCount; i++){
/* the first column is 1, the second is 2, ... */
columnNames[i] = rsmd.getColumnName(i + 1);
Element nextNameNode = document.createElement("name");
Text nextName = document.createTextNode(columnNames[i]);
nextNameNode.appendChild(nextName);
names.appendChild(nextNameNode);
}
列索引從1開始,而非從0開始。讀取每個資料行時,列值都在一個for迴圈中作為字串來泛化地檢索,這個for迴圈將讀取每一個列值:
/* Move the cursor through the data one row at a time. */
while(resultSet.next()){
/* Create an Element node for each row of data. */
Element nextRow = document.createElement("row");
if (debug) System.out.println("new row");
for (int i = 0; i 〈 columnCount; i++){
/* Create an Element node for each column value. */
Element nextNode = document.createElement(columnNames[i]);
/* the first column is 1, the second is 2, ... */
/* getString() will retrieve any of the basic SQL types*/
Text text = document.createTextNode(resultSet.getString(i + 1));
nextNode.appendChild(text);
nextRow.appendChild(nextNode);
}
root.appendChild(nextRow);
}
所有資料都轉換到一個DOM文檔對象中之後,串連就可關閉。DataBaseHandler永遠不需要進行檔案操作。XML文檔是在記憶體中建立的。
一個具體的DataBaseHandler對象
使用少數幾行代碼,即可構造一個泛化的DefaultDataBaseHandler:
public class DefaultDataBaseHandler extends AbstractDataBaseHandler{
public DefaultDataBaseHandler(String urlString, String userName,
String password, String driverName){
setUrlString(urlString);
setUserName(userName);
setPassword(password);
setDriverName(driverName);
}
}
OracleDataBase處理常式稍微有點兒複雜:
public class OracleDataBaseHandler extends AbstractDataBaseHandler{
private String thinOraclePrefix = "jdbc:oracle:thin:@";
private String urlString;
private String userName;
private String password;
private String driverName = "oracle.jdbc.OracleDriver";
private String host;
private String port;
private String sid;
public OracleDataBaseHandler(String host, String sid,
String userName, String password){
this.host = host;
this.sid = sid;
/* a valid url connection string format is: "host:port:sid" */
setUrlString(thinOraclePrefix + host + ":1521:" + sid);
setUserName(userName);
setPassword(password);
}
public OracleDataBaseHandler(String host, String port, String sid,
String userName, String password){
this.host = host;
this.sid = sid;
this.port = port;
/* a valid url connection string format is: "host:port:sid" */
setUrlString(thinOraclePrefix + host + ":" + port + ":" + sid);
setUserName(userName);
setPassword(password);
}
}
ODBCDataBaseHandler區別不大,只是它處理的是資料來源名稱(DSN),而非主機名稱、連接埠和SID。之所以要使用與Oracle資料庫有關的主機名稱、連接埠號碼和SID,是因為我們處理的是一個資料庫伺服器,而不是一個資料庫檔案。但是,Microsoft Access關聯式資料庫要使用.mdb檔案:
public class ODBCDataBaseHandler extends AbstractDataBaseHandler{
private String urlString;
private String userName;
private String password;
private String driverName = "sun.jdbc.odbc.JdbcOdbcDriver";
private String dsn;
private String odbcPrefix = "jdbc:odbc:";
public ODBCDataBaseHandler(String dsn, String userName, String password){
/* a valid url connection string format is: "jdbc:odbc:dsn" */
this.dsn = dsn;
setUrlString(odbcPrefix + dsn);
setUserName(userName);
setPassword(password);
setDriverName(driverName);
}
}
要瞭解訪問一種特定資料庫的細節,請查閱產品文檔。你需要用另一個類來調用一個具體的DataBaseHandler,並進行XSL轉換,從而將資料庫結果轉換成一種有用的替代輸出類型。
SQLMapper
SQLMapper類用一個DataBaseHandler類來完成它的資料庫工作,並用一個映射方法將文檔對象轉換成需要的輸出類型。映射方法返回一個字串,因為原來就假定輸出由字元資料構成。另外,也可以使用一個StringBuffer。
SQLMapper需要一個SQL查詢字串、一個輸出類型集以及一個用於執行具體工作的DataBaseHandler。它們用set方法來初始化,並用get方法來檢索:
if ((getSQL() != null)
&& (getSQL().length() 〉 0)
&& (getOutputType() != null)
&& (isValidOutputType(getOutputType()))
&& (getDataBaseHandler() != null)){
Document document = dataBaseHandler.getDocument(getSQL());
為了轉換成需要的輸出,需要在set方法中指定一個XSL樣式表。我們建立了一個Transformer對象,它只提供一個私人getTransformer方法。該方法可擷取一個預設樣式表或者指定的樣式表。如有必要,可使用Java的TransformerFactory方法來產生一個樣式表:
TransformerFactorytransformerfactory = TransformerFactory.newInstance();
transformer = transformerfactory.newTransformer(getStylesheet());
同樣只需要幾行Java代碼就可完成轉換:
Transformer transformer = getTransformer();
StringWritersw = new StringWriter();
StreamResult result = new StreamResult(sw);
if (transformer != null) {
transformer.transform(new DOMSource(document.getDocumentElement()), result);
output = sw.toString();
System.err.println("output: " + output);
}else{
System.err.println("No Transformer");
}
Transformer對象需要一個DOMSource對象。為了獲得這個對象,我們向Transformer的建構函式傳遞一個DOM文檔的根項目。
最後要由實現者設計自己的XSL樣式表。也可選用一些預設樣式表,以便將未經處理資料轉換成HTML或者XML。下面是一個泛化的XSL樣式表,它能使用所謂的“標識轉換”(identity transformation)技術,將產生的資料轉換成一個XML文檔,並確保輸出內容相容於UTF-8標準,並具有良好的可讀性。
〈?xml version="1.0" encoding="UTF-8"?〉
〈xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"〉
〈xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" /〉
〈xsl:template match="@*|node()"〉
〈xsl:copy〉
〈xsl:apply-templates select="@*|node()"/〉
〈/xsl:copy〉
〈/xsl:template〉
〈/xsl:stylesheet〉
下面是一個泛化的XSL樣式表,它能將產生的資料轉換成一個HTML表格:
〈?xml version="1.0" encoding="UTF-8"?〉
〈xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"〉
〈xsl:output method="html" indent="yes" /〉
〈xsl:template match="resultset"〉
〈h2 align="center"〉Default HTML Transform Result〈/h2〉
〈table border="1" align="center"〉〈xsl:apply-templates/〉〈/table〉
〈/xsl:template〉
〈xsl:template match="names"〉
〈tr〉〈xsl:apply-templates/〉〈/tr〉
〈/xsl:template〉
〈xsl:template match="name"〉
〈td〉〈xsl:apply-templates/〉〈/td〉
〈/xsl:template〉
〈xsl:template match="row"〉
〈tr〉〈xsl:apply-templates/〉〈/tr〉
〈/xsl:template〉
〈xsl:template match="*"〉
〈td〉〈xsl:apply-templates/〉〈/td〉
〈/xsl:template〉
〈/xsl:stylesheet〉
下面是一個泛化的XSL樣式表,它將產生的資料轉換成一個WML(無線傳輸標記語言)表格:
〈?xml version="1.0" encoding="UTF-8"?〉
〈xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"〉
〈xsl:output method="html" indent="yes" /〉
〈xsl:template match="resultset"〉
〈wml〉
〈card id="index" title="Default WML Transform Result"〉
〈xsl:apply-templates/〉〈/card〉
〈/wml〉
〈/xsl:template〉
〈xsl:template match="names"〉
Names: 〈xsl:apply-templates/〉
〈/xsl:template〉
〈xsl:template match="name"〉
〈i〉〈xsl:apply-templates/〉〈/i〉
〈/xsl:template〉
〈xsl:template match="row"〉
〈card〉〈xsl:apply-templates/〉〈/card〉
〈/xsl:template〉
〈xsl:template match="*"〉
〈i〉〈xsl:apply-templates/〉〈/i〉
〈/xsl:template〉
〈/xsl:stylesheet〉
下面是一個泛化的XSL樣式表,它將產生的資料轉換成以逗號分隔的一個表格(CSV表格),它可直接用Excel讀取:
〈?xml version="1.0" encoding="UTF-8"?〉
〈xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"〉
〈xsl:output method="text" indent="yes"/〉
〈xsl:template match="names"〉
〈xsl:for-each select="*"〉
〈xsl:if test="position() != last()"〉〈xsl:value-of select="."/〉,〈/xsl:if〉
〈xsl:if test="position() = last()"〉〈xsl:value-of select="."/〉〈/xsl:if〉
〈/xsl:for-each〉
〈/xsl:template〉
〈xsl:template match="row"〉
〈xsl:for-each select="*"〉
〈xsl:if test="position() != last()"〉〈xsl:value-of select="."/〉,〈/xsl:if〉
〈xsl:if test="position() = last()"〉〈xsl:value-of select="."/〉〈/xsl:if〉
〈/xsl:for-each〉
〈/xsl:template〉
〈/xsl:stylesheet〉
運用SQLMapper的一些思路
後端資料庫可以使用一個表來定義網頁使用者介面的表單元素。在一個JSP頁中,只需幾行代碼即可檢索這個表。你的Web伺服器也許安裝了一個Oracle資料庫,但是沒有前端圖形化使用者介面。現在就可以定義一個GUI的元素,讓資料庫正式工作起來:
〈%@ page language="java" contentType="text/html" import="sqlmapper.*, mywebapp.* %〉
〈%@ page errorPage="errorPage.jsp" %〉
〈html〉
〈!-- getUserArea.jsp executed on 〈%= (new java.util.Date()) %〉 --〉
〈!-- @Author: Charles Bell --〉
〈!-- @Version: April 22, 2003 --〉
〈head〉
〈title〉Your company name - 〈%= dynamicTitle %〉〈/title〉
〈/head〉
〈body background="〈%= dynamicBackgroundImageFileName%〉"〉
〈%
WebAppUtilitymyWebAppUtility = new WebAppUtility();
String host = myWebAppUtility.getDatabaseHost();
String sid = myWebAppUtility.getDatabaseSID();
String userName = (String) session.getAttribute("validatedUserName");;
String password = myWebAppUtility.getDatabasePassord();
SQLMapper mapper = new SQLMapper();
DataBaseHandler dataBaseHandler=
new OracleDataBaseHandler(host, sid, userName, password);
mapper.setSQL("select * from FormDataElements");
mapper.setOutputType("html");
mapper.setDataBaseHandler(dataBaseHandler);
mapper.setXSLTranformStyleSheet("stylesheets/formdata.xsl");
out(mapper.map);
%〉
〈%@include file="footer.jsp" %〉
〈/body〉
〈/html〉
使用這些技術,只需單擊一個按鈕,JSP網頁即可彈出最新的、根據一個活動資料庫而動態產生的報表。CSV輸出可用於產生動態Excel試算表。XML輸出可為另一個Web應用程式提供服務,該應用程式將與它自己的後端資料庫進行通訊。
小結
我們介紹了如何建立一個介面來定義DataBaseHandler的泛化行為,並用一個抽象類別來實現它,以便將這個抽象類別擴充成一個定製的、具體的資料庫處理常式,所有操作只需幾行Java代碼。SQLMapper類利用這種行為來透明串連一個關聯式資料庫,執行SQL查詢,並將資料轉換成一個DOM文檔對象。然後,通過應用一個XSL樣式表來擷取需要的輸出,從而完成一個DOM對象的轉換。隨後,輸出可由任何應用程式使用,只需採用一種有效、易於實現的方式來提供需要的輸入。