`
zhangziyangup
  • 浏览: 1078662 次
文章分类
社区版块
存档分类
最新评论

DB2 XML 编程,第 3 部分: 客户机中的 XML 编程

 
阅读更多

简介

Web 2.0 日益流行在很大程度上是由于当代 Web 浏览器有很强的灵活性。这些浏览器使用 Ajax 与服务器通信来交换 XML 数据,然后利用 DHTML 在 XML 中导航并将数据显示给用户。浏览器的强大功能使它们能够作为基于 Web 的应用程序中的瘦客户机。

以这些技术为基础,产生了许多新技术和项目来满足 Web 2.0 的需要。新的辅助库(比如 Dojo 和 Sarissa)可以简化使用 XML 的过程,还增加了对 Scalable Vector Graphics(SVG)的支持。为了满足 Web 2.0 的需要,还开发了可嵌入的小部件(比如 Google 和 Yahoo Maps)、数据联合和集成方法(比如 RSS 和 Atom feed)以及使用数据 mashup 的新型开发方式。

如图 1 所示,连接所有这些技术的都是 XML。提要中的 XML 使提要能够通过网络发布和接收复杂的数据结构。DHTML 中的 DOM 应用程序编程接口(API)和 Sarissa 中的 XPath 支持可以在客户机中执行高效的 XML 导航,从而进行读写操作。


图 1. 面向 Web 的体系结构
面向 Web 的体系结构

注意:在以提要和服务形式查看数据源的 Web 2.0 环境中,数据库驱动程序扩展为支持 REST、FEED 和 SOAP 调用。如果解决了安全隐患,就能够从应用服务器和 Web 客户机使用 SOAP 或 REST 调用直接访问数据库例程,而不必创建不必要的映射。在下一篇文章中,将为 DB2® 创建一个 SOAP 驱动程序示例。

在客户机中使用 XML 模型

因为许多 XML 周边技术在应用服务器和客户机上都存在,所以也可以将 第一篇文章 中的 XML 数据模型扩展到客户机。在本文中,学习如何扩展 XML 模型,从而使用来自应用服务器的 XML 数据创建功能丰富的客户机。了解如何使用 DHTML 显示 XML 数据,用 XPath 进行导航,用 DOM 修改 XML 数据、序列化并返回给应用服务器。

与在应用服务器中创建的 DOM 包装器相似,我们将为客户机创建一个 DOM 包装器。这个包装器不但将 JavaScript 显示和业务逻辑代码与 DOM API 隔离开,还使代码与应用服务器层中的 Java ™ 代码非常相似。为此,我们将使用开放源码库 Sarissa,这个库为浏览器中运行的 JavaScript 代码提供了 XPath API。本文后面提供了包装器代码(见 下载)。

<scripttype="text/javascript"src="sarissa/sarissa.js"></script>
<scriptlanguage="javascript"type="text/javascript"src="xmlparse.js"></script>

场景

下面几节继续使用本系列前几篇文章中的保险示例(见 参考资料)。Web 客户机提供以下功能。

1. 允许用户查看和修改个人信息中的名字和姓氏。

2. 根据用户选择的保险公司,计算购买的每个商品的保险费率。

一定要注意:本文和前几篇文章中的代码只用于解释概念。只有解释 XML 模型的部分编写了完整的代码。逻辑的其他部分是伪代码,读者可以推导出功能所需的标准代码。下一篇文章讨论一个完整的业务场景,包含完整的代码。

允许用户查看和修改他们的姓名。

从应用服务器获得客户信息并在客户机上显示。

客户机 JavaScript 代码

profilediv 用来将从 XML 数据创建的 HTML 字符串显示给用户。

<div class="profilediv" id="profilediv"></div>


当用户登录站点时,输入客户 id(cid)。当用户在客户机中选择 Update profile 时,将这个客户 id 传递给 getCustomerInfo 函数。客户机使用 Sarissa 库提供的 XMLHttpRequest API 向服务器发送一个 HTTP 请求。在这个请求中,还定义回调函数。当服务器产生响应时,会调用函数 customerInfoCallback。清单 1 演示如何获取客户信息:


清单 1. 客户机请求客户信息

<script>
functiongetCustomerInfo(cid)
...{
varxmlhttpObj=newXMLHttpRequest();
varaddr=hosturl+"?cmd=getuserprofile&msg="+cid;
varxmlhttpObj=newXMLHttpRequest();
xmlhttpObj.open(
'GET',addr,true);
xmlhttpObj.onreadystatechange
=function()...{customerInfoCallback(xmlhttpObj);};
xmlhttpObj.send(
"");
}

应用服务器 Java 代码

清单 2 显示当应用服务器接收客户机 HTTP 请求时发生的情况。服务器首先检查 HTTP 请求是 POST 还是 GET,然后根据这个信息,从 HTTP 调用的参数获得命令和数据值。


清单 2. 应用服务器接收请求

publicvoidservice(HttpServletRequest_req,HttpServletResponse_res)
throwsServletException,IOException
...{

Stringcmd,msgtext,returnvalue;

if(_req.getMethod().equalsIgnoreCase("POST"))
...{
Stringmessage
=getPostBody(_req.getReader());
XMLParsemsgxml
=newXMLParse(message);
cmd
=msgxml.getValue("//request/@cmd");
msgtext
=msgxml.toString("/request/*");
}

else
...{
cmd
=_req.getParameter("cmd");
msgtext
=_req.getParameter("msg");
}

如果将上面的代码与第 2 部分的 customerItemsWithInsurance 存储过程中的查询进行对比,就会发现两者之间有许多相似之处,尤其是使用的 XPath。这再次说明了使用 XML 模型的优点和将业务逻辑放在数据库中的简便性。

还要注意 XPath 表达式在搜索和导航层次化 XML 数据模型 方面的能力。如果在对象数据模型 中使用 Java 代码实现同样的搜索,就需要做大量工作。使用 XPath 简化了这个过程,只需使用一个字符串表达式。

客户机 JavaScript 代码

服务器调用 itemsPurchasedCallback 函数,在这个函数中使用 DOM 包装器解析返回的 XML 数据。清单 12 演示具体做法:


清单 12. 使用 DOM 包装器解析 XML

functionitemsPurchasedCallback(xmlhttp)
...{

if(xmlhttp.readyState==4&&xmlhttp.status==200)
...{
varitemInfo=newxmlparse(xmlhttp.responseXML,false);

首先从返回的 XML 中提取出客户名:


清单 13. 提取客户名

varfirstname=userinfo.getValue("/Customer/@firstname",null);
varlastname=userinfo.getValue("/Customer/@lastname",null);

varhtmlstr="<tableclass='inputtable'>"
htmlstr
+='<tr><td>firstname:<tdcolspan=5>'+firstname
htmlstr
+='<tr><td>lastname:<tdcolspan=5>'+lastname

然后,循环遍历文档中的所有商品并提取相关信息,创建用来向用户显示信息的 HTML 字符串。


清单 14. 生成 HTML

itemInfo.find("//item",null,true);
htmlstr
+='<tr><td>itemID<td>description<td>date<td>price<td>insurance';
for(varj=0;itemInfo.currentFind.length>j;j++)
...{
varid=itemInfo.getValue("@ID",j);
vardescription=itemInfo.getValue("@description",j);
varpurchaseDate=itemInfo.getValue("@purchaseDate",j);
varprice=itemInfo.getValue("@price",j);
varinsurance=itemInfo.getValue("insurance/text()",j);
varcurrency=itemInfo.getValue("insurance/@currency",j);
htmlstr
+='<tr><td>'+id+'<td>'+description+'<td>'+purchaseDate+
'<td>'+currency+price++'<td>'+insurance;
}

document.getElementById(
"profilediv").innerHTML=htmlstr;
}

注意,使用 XPath 搜索和遍历商品元素的客户端 JavaScript 代码和前面定义的应用服务器 Java 代码很相似。

对象数据模型

即使使用标准的对象数据模型方法,应用程序层数据对象仍然需要序列化为客户机可以理解的一种格式。如果对象很复杂,那么基本上有两种方式:

1. 在应用程序层中生成客户机页面并在 Web 页面的适当部分中填写数据(ASP/JSP 方式)。然后公开多个 API 来接受用户对数据的任何修改。这种方式的问题是:

  • 它将客户机和应用服务器代码混在一起,导致一种糟糕的体系结构;
  • 它要求创建多个 API 并提供 API 的文档记录,这样客户机才能传递回对数据的修改。

2. 生成一个数据结构,可以将它作为字符串发送给客户机。最适合表示这种序列化数据结构的格式是 XML。这种方式的问题是:

  • 必须编写额外代码来执行序列化。
  • 如果来自客户机的更新后数据是 XML,那么应用服务器中需要用更多代码向应用程序数据对象解释修改后的 XML。

如果必须用 XML 数据模型与客户机进行通信,那么在应用程序层本身中为什么不使用 XML 数据模型呢?

我们仍然需要操作保险费率 Web 服务的结果。因为这一信息是 XML 文档,所以需要先分解它并将信息放到对象模型中。如果数据在数据库中保存为 XML 格式,就可以将 XML 传递给 customerItemsWithInsurance 存储过程。这种方式的问题是,如果以 XML 格式存储数据,而在应用程序中使用对象模型,就会产生各种映射和转换问题。

如果数据在数据库中存储为关系形式,对数据库的查询会变得更复杂。另外,必须在中间层中编写更多代码,将关系数据映射到对象数据模型或 XML 数据模型。

结束语

在面向 Web 的体系结构中,数据需要序列化,然后才能在应用程序的不同层之间传递。对于在 Web 客户机和应用服务器之间传输复杂的数据,最合适的方式是 XML。在每一层中都可能需要查询、转换和更新数据,如果保持数据的 XML 本质,就可以使用基于 XML 的技术(比如 XPath、DOM 和 XQuery)操作数据。XML 数据模型编程方式可能成为解决业务数据集成问题的万能方法。

下载

描述 名字 大小 下载方法 用于 Javascript 的 DOM 和 XPath 包装器类
xmlparse.zip 9 KB HTTP
关于下载方法的信息


参考资料

学习

因为命令是请求获得用户的个人信息,所以应用服务器访问数据库中的客户信息,并将信息返回给客户机。清单 3 只显示获得此信息所需的 SQL 语句;读者应该知道如何使用 Java Database Connectivity(JDBC)API 连接数据库并获得查询结果。

因为客户信息在数据库中存储为 XML 文档,所以在将它返回给客户机之前不需要执行任何转换。注意,在 HTTP 报头中,返回数据的内容类型设置为 XML。


清单 3. 应用服务器的响应

if(cmd.equalsIgnoreCase("getuserprofile"))
...{
//returnvalue=selectCUSTXMLfromcustomer_tablewherecustomerid=msgtext
}

_res.setContentType(
"text/xml");
_res.getWriter().write(returnvalue);
_res.setHeader(
"Cache-Control","no-cache");

客户机 JavaScript 代码

当客户机收到应用服务器的响应时,调用 callback 事件中定义的函数。

清单 4 显示对 userinfo 的请求。注意,userinfo 声明为全局变量,因为当发送用户对姓名的更新时要修改这个变量。


清单 4. 对 userinfo 的请求

varuserinfo=null;
functioncustomerInfoCallback(xmlhttp)
...{
if(xmlhttp.readyState==4&&xmlhttp.status==200)
...{

因为服务器返回的数据是 XML,所以将它直接传递给 DOM 包装器的构造器。

userinfo= new xmlparse(xmlhttp.responseXML, false);

接下来,使用 XPath 从 XML 中提取出姓名。

var firstname = userinfo.getValue("/Customer/@firstname",null);
var lastname = userinfo.getValue("/Customer/@lastname",null);

清单 5 演示如何使用 DHTML 创建一个用于修改客户名称的图形用户界面(GUI),并将它插入 HTML 页面中声明的 div 标记 profilediv


清单 5. 创建 GUI 的 DHTML

varhtmlstr="<tableclass='inputtable'><tr>";
htmlstr
+='<td>firstname:</td><td><inputid="fname"value="'+firstname+'"/></td>';
htmlstr
+='<td>lastname:</td><td><inputid="lname"value="'+lastname+'"/></td>';
htmlstr
+='<tr><td/><td><inputtype="button"value="save"
onClick="javascript:updateCustomer()"/></td>
';
htmlstr
+='</tr></table>';
document.getElementById(
"profilediv").innerHTML=htmlstr;
}
}

完成修改之后,单击 Save 按钮来调用 updateCustomer 函数。用修改信息更新包含客户信息的 DOM userinfo。这里同样使用 XPath 导航到需要修改的数据节点。


清单 6. 使用 XPath 更新客户信息

functionupdateCustomer()
...{
varfname=document.getElementById("fname").value;
varlname=document.getElementById("lname").value;
userinfo.setValue(
"/Customer/@firstname",null,fname);
userinfo.setValue(
"/Customer/@lastname",null,lname);

创建一个新请求,将更新后的 DOM 序列化并附加到请求中。清单 7 显示发送给(POST)服务器的请求字符串。

注意,因为消息格式是 XML,所以请求的 HTTP 报头中的内容类型设置为 XML。


清单 7. 发送给服务器的请求字符串

varmsg='<requestcmd="updateuserprofile">'+userinfo.toString("/")+'</request>';
varxmlhttpObj=newXMLHttpRequest();
xmlhttpObj.open(
'POST',hostname,true);
xmlhttpObj.onreadystatechange
=function()...{profileUpdated(xmlhttpObj);};
xmlhttpObj.setRequestHeader(
'content-type','text/xml');
xmlhttpObj.send(msg);
}

应用服务器 Java 代码

使用 DOM 包装器解析请求中的客户 XML 数据。

因为 customerid 是原来发送给客户机的客户个人信息中的一个属性,所以在请求中不需要为客户 ID 发送额外参数。

清单 8 演示如何从 XML 中提取出 customerid 并用它创建更新数据库的 SQL 语句。来自 HTTP 请求的客户个人数据作为参数传递给更新语句;不需要修改。


清单 8. 创建 SQL 更新语句

elseif(cmd.equalsIgnoreCase("updateuserprofile"))
...{
XMLParsecustxml
=newXMLParse(msgtext);
Stringcid
=custxml.getValue("/Customer/@customerid");
//updatecustomer_tablesetcustxml=?wherecustomerid=cid
//stmt.setString(1,msgtext);
}

注意,在客户机和服务器中用来导航 XML 的应用程序代码是相似的。另外,对于两个 HTTP 请求,应用服务器仅仅作为客户机和数据库之间的中介 —— 在交换过程中并不操作数据。

计算购买的商品的保险费率

用户从一个包含所有保险公司名称的下拉列表中选择一家保险公司。保险公司提供一个 Web 服务,可以查询这个服务来获得当前的保险费率。费率信息以 XML 文档的形式提供,应用程序使用此信息计算每种商品的保险费。

客户机 JavaScript 代码

还从列表中选择保险公司提供的 Web 服务的 URL。因为 Ajax 不允许 URL 重定向,所以需要从应用服务器调用 Web 服务。将 URL 放在请求中传递给应用服务器,请求计算购买的每种商品的保险费。清单 9 演示这个过程:


清单 9. 客户机选择 URL

functionitemsPurchased(url,cid)
...{
varmsg='<requestcmd="getPurchaseInfoWithInsurance"><datacustomerid="cid">
<![CDATA[
'+url+']]></data></request>';

varxmlhttpObj=newXMLHttpRequest();
xmlhttpObj.open(
'POST',hostname,true);
xmlhttpObj.onreadystatechange
=function()...{temsPurchasedCallback(xmlhttpObj);};
xmlhttpObj.setRequestHeader(
'content-type','text/xml');
xmlhttpObj.send(msg);
}

}

注意:因为 URL 可能包含特殊字符,所以我们把它嵌入在 CDATA 部分中,以避免请求 XML 变得怪异。

应用服务器 Java 代码

使用 DOM 包装器解析请求消息并从其中提取出保险服务 URL。清单 10 演示应用服务器如何使用这个 URL 调用保险公司的 Web 服务,获取包含保险费率的 XML 文档。


清单 10. 应用服务器获取保险费率

elseif(cmd.equalsIgnoreCase("getPurchaseInfoWithInsurance"))
...{
XMLParsedataxml
=newXMLParse(msgtext);
Stringurl
=dataxml.toString("/data/text()");
Stringinsurancestr
=callWebServiceUsingHTTPClient(url);

注意:在 第 2 部分 中的 “一个更精细的示例” 一节中,定义了保险费率 XML。

再次从消息中提取出客户 ID,并用它在数据库中查询这位客户购买的商品的相关信息。

String cid= dataxml.getValue("/data/@customerid");

创建购买的商品列表的业务逻辑可以以两种方式之一实现:

  1. 调用 第 2 部分的清单 6 中创建的数据库存储过程。
    // returnvalue = call customerItemsWithInsurance (cid, insurancestr);
    

  2. 使用第一篇文章中的代码在应用服务器中编写逻辑。使用 第 1 部分的清单 6 中的 customerXML,循环遍历每个商品、计算保险费并添加到商品信息中。

    用以下代码替换原来代码中的 9-11 行:



    清单 11. 替换 9-11 行的代码
    XMLParseinsurancexml=newXMLParse(insurancestr);
    customerXML.find(
    "/Customer/Items/item",true);
    Stringcurrency
    =insurancexml.getValue("//rate/@currency");

    for(inti=0;customerXML.currentFind.getLength()>i;i++)
    ...{
    price
    =customerXML.getValue("@price",i));
    if(price>500)rate=insurancexml.getValue("//rate[@price=""]/@rate"));
    elseIf(price>100)rate=insurancexml.getValue("//rate[@price="500"]/@rate"));
    elserate=insurancexml.getValue("//rate[@price="100"]/@rate"));
    Stringiteminsurance
    ="<insurancecurrency="+currency+">"+price*rate+"</insurance>"
    customerXML.appendElement(customerXML.createNode(iteminsurance),
    customerXML.getNode(
    null,i),false)
    }

    //returnvalue=customerXML.toString();
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics