使用DOM和XSL来格式化由Java提取的数据

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对象的转换。随后,输出可由任何应用程序使用,只需采用一种有效的、易于实现的方式来提供需要的输入。

时间: 2024-11-08 18:19:15

使用DOM和XSL来格式化由Java提取的数据的相关文章

java提取oracle数据的问题

问题描述 oracle里面有一组数据,有一个时间字段,我想要用java按照时间的间隔提取数据,不知道有没有好的方法?求大神解惑 解决方案 解决方案二:取出数据,利用程序getTime()计算时间差.解决方案三:是按照时间查询还是过一段时间取一组数据?解决方案四:用定时器写一个程序定时的执行某一根据时间段变化的sql然后取出数据

mysql-angularJS如何与JAVA后台传数据

问题描述 angularJS如何与JAVA后台传数据 java是ssh框架写的,在action里返回值试了string,jsonobject,jsonarray都不行,不知道是我方式错了还是类型错了.求大牛指点,最好有实际可参考,谢谢. 解决方案 http://www.simplecodestuffs.com/struts2-angularjs-integration/

正则验证-java 正则匹配数据中的金钱数字

问题描述 java 正则匹配数据中的金钱数字 A文件数据:1001分店20121231,元旦之际祝您新年快乐身体健康万事如意.您共欠担保公司¥103959.50元.请您尽快偿还欠款,以免扩大损失.您的借款已逾期6天,欠款金额为1988.20元,逾期将影响信用记录,请即刻缴纳.截止 2015年07月01日 您应还金额为:4128元.如有疑问,请联系王先生1101101010.你好,截至到昨天夜里23:59,你欠款34532.32元,请您尽快偿还欠款,以免扩大损失.你好,截至到昨天夜里23:59,你

java提取字符串的正则表达式

问题描述 java提取字符串的正则表达式 数据格式:{""collectTime"":""2015-11-11 10:08:27""dateTime"":""1447207707612hostName"":""SW-ADMIN-S5750-A0102""instanceId"":""260

服务端无法接收到数据-iOS 服务端java接收到数据为空

问题描述 iOS 服务端java接收到数据为空 我们的服务端,忽然无法接收到我post 传过去的json数据,以前好几个月都没问题,我的代码大致如下,求大神解救 self.manager = [AFHTTPRequestOperationManager manager]; self.manager.responseSerializer = [AFJSONResponseSerializer serializer]; self.manager.requestSerializer=[AFJSONRe

解决方案-JAVA cxf 返回数据问题

问题描述 JAVA cxf 返回数据问题 基本接口知道怎么写,问题在 我想实现 客户端 提交 一句SQL JAVA接口实现 return 整个查询后的数据, 百度上很多 都是 需要 单独建立一个 对象实例类 然后 返回LIST ,但 查询SQL返回 的数据不固定,每个表实例化 不太现实,求解决方案 最好能自由定义返回数据格式,或者自动定义, 还有就是 会话方式 能否使用SESSION 保存状态? 解决方案 你可以写一个工具类,把sql语句弄成一个变量,每次使用的时候调用这个方法把sql语句传进去

java中excel数据写入数据库

问题描述 java中excel数据写入数据库 java中excel数据写入数据库 : 通过上传excel同时把里面数据写入数据库.struts框架 源码,急用 解决方案 http://www.cnblogs.com/hongten/archive/2012/02/22/java2poi.html 解决方案二: 需要导入jxl.jar (在网上可以下载,然后放到lib文件夹中) 通过java操作excel表格的工具类库 支持Excel 95-2000的所有版本 生成Excel 2000标准格式 支

怎么用simple html dom获取div块内table里的tr数据

问题描述 怎么用simple html dom获取div块内table里的tr数据 <div id="id"> <style> ... } </style> <div class="tips-a tips-a-1" style="display: none;"> ... </div> <div class="tips-a tips-a-2" style=&quo

java 提取指定网页中的所有汉字

问题描述 java 提取指定网页中的所有汉字 读取这个网页中汉字 http://news.sina.com.cn/c/2015-03-24/103831638858.shtml?qq-pf-to=pcqq.c2c 有大神也个例子么 解决方案 要求要用到正则表达式 解决方案二: 如果你这个请求是固定的格式,你可以选择截取URL