深蓝海域KMPRO

MSDN Online Voices - Extreme XML:已启用XML Web服务的Office文档

2002-09-13 13:41

MSDN Online Voices - Extreme XML:已启用XML Web服务的Office文档

 

Microsoft Office XP 和 .NET Web 服务已经联姻,您准备好了吗?在 B2B 电子商务网络世界中,为什么不将业务处理流程同人们在计算上所做的一切结合起来,从而将 Web 服务的强大动力传递到最终用户?我在谈论什么?哦,我在谈论一个看起来有些象图 1 所示的 Excel 电子表格。

 
图 1:已启用 Web 服务的 Excel 电子表格

这可不是一份普通的电子表格。它使用 UDDI 来查找公司的地址,并使用目录 Web 服务来查找产品信息。当您单击“发送”按钮后,它还会对 XML 电子表格格式进行 XML 转换,生成一个 RosettaNet(英文)PIP 3 A4 订单申请格式。

键入要购买其商品的公司名称后,单击“查找”按钮,电子表格下的某个 VBA 代码将调用 UDDI 并完成地址部分其余的内容。例如,键入 Microsoft,单击“查找”,您将在“卖方”域中看到下面的内容:

 
图 2:“卖方”域

键入数量,例如 23,并在说明域中键入 Pear,然后按 Tab,某个 VBA 代码将查询 SOAP 目录 Web 服务,以查看是否有匹配的产品,并列出详细信息。在本例中,我已将目录 Web 服务连接到 Northwind 数据库,因此它将返回以下信息:

 
图 3:电子表格订购部分的详细情况

在本例中,还列出了说明并将其转换为一个连接到详细介绍此产品的 HTML 页面的链接。

如果找到多个产品但没有一项完全满足您键入的条件,则将提供一个选项下拉列表。例如,如果键入豆腐,您将看到下列选项:

 
图 4:未找到完全匹配项时所提供的多个选项的示例

如果您选择其中一项,则将提供此项的详细信息。

完成后,单击“发送”按钮,即生成 RosettaNet PIP 3 A4 XML 订单格式,订单也同时发出。

如何实现?

通过访问“工具”菜单,选择“宏”,然后选择“Visual Basic 编辑器”,您可以浏览 VBA 代码。ThisWorkbook 下的某些代码会对电子表格中的变更做出响应,特别是当您删除说明时,Workbook_SheetChange 事件将清除一个项目行;当您将“说明”域移入“SKU”域时,Workbook_SheetSelectionChange 事件将调用 FindProduct()。如果 FindProduct 返回 XMLNode,将从该节点拉出相关的域,以填充项目行的其他详细信息。

有关 UDDI find_business 调用的工作方式,您可以查阅我的另一篇文章 UDDI:一种 XML Web 服务。如果找到一项业务,在 UDDI 响应的 /businessInfo/contacts/contact/address/ 部分找到的 addressLines 将被用来填充“卖方”地址块。

目录 Web 服务

Catalogs 模块中的 FindProduct 函数将使用包含搜索项的 URL 参数来调用目录服务 URL。它会得到一个 SOAP 响应并首先检查是否与 /Envelope/Body/Fault 匹配,如果返回的不是 Fault,它将继续打开 <CatalogQueryResult>,以检查返回项目中的 ProductName 属性是否与给定的范围相匹配。它还会在可视区域以外的页面创建下拉选项列表。您可以在“数据”菜单中选择“验证”来查看下拉列表的工作方式。

目录 Web 服务十分简单。.aspx 入口点将创建一个 CatalogSearch 对象,该对象在 search.cs 中定义,并调用 Execute 按下列方式传递 HttpResponse 输出流:

<%@Language="C#" src="search.cs"  Debug="true" %>

<%
 
Response.ContentType = "text/xml";
  
string term = Request.QueryString["term"];
  
if (term != null) {
     
CatalogSearch s = new CatalogSearch(term);
     
s.Execute(output);
  
} else {
     
Response.Write("<Empty/>");
  
}

%>

真正的乐趣将从 Execute 方法开始。它是一个捆绑在 XmlTextWriter 中的、十分简单的 SQL 托管提供程序代码,能够从 SQL SELECT 语句返回特定的域。因此,它基本上是一个通过 DataReader 向 XmlTextWriter 写入以下内容的 while 循环:

public void Execute(TextWriter stm)

{     
  
XmlTextWriter xw = new XmlTextWriter(stm);
  
xw.WriteStartElement("Envelope", "
http://schemas..../envelope/");
  
xw.WriteStartElement("Body", "
http://schemas..../envelope/");
  
try {
     
String const = "server=localhost;uid=sa;pwd=;database=northwind";
     
SQLConnection con = new SQLConnection(constr);
     
con.Open();
     
IDataReader reader;
     
String query = "SELECT ProductName,UnitPrice,QuantityPerUnit," +
          
"SupplierID,ProductID FROM Products WHERE " +
          
"ProductName LIKE '%" + term + "%'";
     
SQLCommand cmd = new SQLCommand(query, con);
     
cmd.Execute(out reader);
     
string funNamespace = "urn:schemas-b2b-fun:catalogs";
     
xw.WriteStartElement("CatalogQueryResult", funNamespace);
     
while (reader.Read())
     
{
        
xw.WriteStartElement("item");
        
xw.WriteAttribute("ProductName", reader.GetString(0));
        
xw.WriteAttrDecimal("UnitPrice", reader.GetDecimal(1));
        
xw.WriteAttribute("UnitOfMeasure", reader.GetString(2));
        
xw.WriteAttribute("SKU", "S"+reader.GetInt32(3)+

"-P"+reader.GetInt32(4));
        
xw.WriteEndElement();
     
}
     
xw.WriteEndElement();
     
con.Close();
  
} catch (Exception e) {
     
xw.WriteStartElement("Fault");
        
xw.WriteElementString("faultcode","500");
        
xw.WriteElementString("faultstring",e.ToString());
     
xw.WriteEndElement();
  
}
  
xw.WriteEndElement();
  
xw.WriteEndElement();
 
xw.Close();

}

URL http://localhost/catalog/search.aspx?term=豆腐将返回以下结果:

<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">

<Body>
 
<CatalogQueryResult xmlns="urn:schemas-b2b-fun:catalogs">
   
<item ProductName="豆腐" UnitPrice="23.25"
 
UnitOfMeasure="每包装 40 - 100 克" SKU="S6-P14"/>
   
<item ProductName="长保鲜期豆腐" UnitPrice="10"
 
UnitOfMeasure="每包装 5 千克" SKU="S4-P74"/>
 
</CatalogQueryResult>

</Body>

</Envelope>

这可能是使用 .NET 框架从 SQL Server 获得 XML 的最有效的方式。粗略计算,在我的 Dell PowerEdge 2400 上每秒可获得其中的 80 到 90 个这样的 XML。

“发送”按钮

SendOrder() 函数可按电子表格中选定范围的单元格的 XML 表示形式加载 XML 文档。通过下列神奇的 VBA 代码行便可以实现这一切:

With ActiveSheet

Set sourcexml = New MSXML2.DOMDocument
 
sourcexml.loadXML .Range("B1:N34").value(xlRangeValueXMLSpreadsheet)
End With

这将返回一个巨大的 XML 程序块,完整说明有关电子表格中选定范围的单元格的全部信息。以下是 XML 程序块的代码片断:

<Workbook>
   
<Worksheet>
       
<Table>
           
<Row>
               
<Cell ss:StyleID="s23"><Data ss:Type="Number">23</Data>

<NamedCell ss:Name="Item"/></Cell>
 
<Cell ss:MergeAcross="4" ss:StyleID="m31209522"

ss:HRef="
http://eshop.msn.com/category.asp?catId=170">

<Data ss:Type="String">鲍勃叔叔的有机干梨

</Data></Cell>

<Cell ss:StyleID="s52">

<Data ss:Type="String">S3-P7</Data></Cell>
               
<Cell ss:StyleID="s26">

<Data ss:Type="Number">30</Data>

<NamedCell ss:Name="UnitPrice"/></Cell>
               
<Cell ss:StyleID="s27">

<Data ss:Type="String">每包装 1 磅(12 个)</Data></Cell>

<Cell ss:StyleID="s37">

<Data ss:Type="Number">690</Data></Cell>
               
<Cell ss:StyleID="s49"/>
           
</Row>
       
</Table>
   
</Worksheet>

</Workbook>

我们可以使用 XSL 将它转换为以下格式:

<PurchaseOrder xmlns="http://www.rosettanet.org">
  
<deliverTo>
     
<PhysicalAddress>
        
<cityName>Seattle, WA, USA 98111</cityName>
        
<addressLine1>Airport Chocolates</addressLine1>
        
<addressLine2>2711 Alaskan Way</addressLine2>
        
<regionName>USA</regionName>
     
</PhysicalAddress>
  
</deliverTo>
  
<ProductLineItem>
     
<ProductQuantity>23</ProductQuantity>
     
<productUnit>
        
<ProductPackageDescription>
           
<ProductIdentification>
              
<GlobalProductIdentifier>S3-P7</GlobalProductIdentifier>
           
</ProductIdentification>
        
</ProductPackageDescription>
     
</productUnit>
     
<Description>鲍勃叔叔的有机干梨</Description>
     
<requestedPrice>
       
<FinancialAmount>
           
<GlobalCurrencyCode>USD</GlobalCurrencyCode>
           
<MonetaryAmount>30</MonetaryAmount>
        
</FinancialAmount>
     
</requestedPrice>
  
</ProductLineItem>
  
<thisDocumentGenerationDateTime>
     
<DateTimeStamp>2001-03-15T00:00:00.000</DateTimeStamp>
  
</thisDocumentGenerationDateTime>

</PurchaseOrder>

注意:按照 RosettaNet PIP 3 A4 订单申请规范,这可能不是一份技术上完整的申请,但您可以由此得到启发。

提高此转换的强壮性的技巧在于命名要从中导出数据的重要单元格。这可以通过 XSLT 转换中下列样式的 XPath 表达式来实现:

 select="/Workbook/Worksheet/Table/Row/Cell[NamedCell[@ss:Name='City']]

此特定表达式将查找名称为 City 的单元格。而样式表的其余部分将被忽略。有关详细信息,请参阅 XLToPO.xsl。

尝试使用

要运行此函数,您只需安装 MSXML 3.0 并获取一份 Northwind 数据库。演示代码将按以下方式连接至 SQL Server:

SQLConnection("server=localhost;uid=sa;pwd=;database=northwind");

如果 Northwind 数据库位于其他位置,您需要更改此代码位。

PO.xsl 电子表格假定目录服务位于:

http://localhost/catalog/search.aspx

您需要在本地计算机的名为 catalog 的虚拟目录中安装 Web 服务 search.aspx、search.cs 和 XLToPO.xsl,或者将电子表格指向别处。

要编辑电子表格,您必须关闭保护(可以使用“工具/保护”子菜单关闭)。

下一步

您可能希望将供应商的目录服务绑定保存到 UDDI 中。有一些 VBA 代码能帮您做到这一点。该代码将查找可识别的目录服务 serviceInfo(通过 serviceKey),如果找到,代码将使用包含在 serviceDetails 中的 accessPoint。我在本演示中所使用的伪目录 API 并没有作为 UDDI 中的已知服务类型进行注册。

使用 Office Smart Tag 来做类似的工作可能会是一种比较有趣的体验。有关详细信息,请参阅 http://msdn.microsoft.com/office/(英文)上的 Smart Tag SDK。

相关推荐