首页 > 开发 > Java > 正文

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

2024-07-13 09:55:21
字体:
来源:转载
供稿:网友

商业源码热门下载www.html.org.cn

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对象的转换。随后,输出可由任何应用程序使用,只需采用一种有效的、易于实现的方式来提供需要的输入。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表