现在我正在手动地将 DataSet 映射为静态类型 Author(我可能只在这里使用 DataSet 检索数据库中的数据,直接使用 IDataReader 才更有意义,因为它提供的解决方案效率更高)。 在这种情况下,在 Author 类和等价的 XML 架构定义之间会自然形成一个映射,而架构映射也会很好地映射回一个等价的 Java 类(如前所示的 CalculateMortgage)。
现在有了这种方法,Axis 开发人员就可以在编写代码时无需直接接触 XML API。 要实现这一点的另一种方法是使用由 XML 架构定义生成的类型化 DataSet,而不是使用通用的 DataSet。
类型化 DataSets
类型化 DataSets 是派生自 DataSets 的类,它们公开了说明特定数据视图的强类型成员。 与通用 DataSets 不同,类型化 DataSets 在设计时限定于特定结构(XML 架构定义)。 使用 xsd.exe 或 Visual Studio? .NET 内置的设计器,可以从 XML 架构定义自动地生成类型化 DataSets。
包含 XML 架构定义,该架构定义说明了将要从 Web 服务返回的作者纪录。 使用 /dataset 开关(或Visual Studio .NET 设计器),通过 xsd.exe 运行该架构后,就会得到派生自 DataSet 的名为 AuthorSet 的类,如下所示:
[Serializable()]
public class AuthorSet : DataSet {
private authorsDataTable tableauthors;
... // strongly typed accessors
}
基础结构知道对于该类型化 DataSet 架构定义应该是什么,所以不必再在运行时提供架构(虽然它仍然这么做)。 现在,可以新建返回 AuthorSet 对象的 WebMethod:
[WebMethod]
public AuthorSet GetAuthorsAsTypedDataSet()
{
AuthorSet aus = new AuthorSet();
sqlDataAdapter1.Fill(aus);
return aus;
}
现在,基础结构有足够的信息可以在设计时通知客户端所返回的集合中的作者记录的结构。 但是,当 ASP.NET 基础结构生成 WSDL 和 XML 架构定义时,它会导入类型化 DataSet 的 XML 架构定义并使用一个受限通配符。
因为不再存在对 s:schema 元素的引用,WSDL2Java 能够成功地生成 Java 类。 因为导入语句允许其访问 AuthorSet 的 XML 架构定义,它可以生成代表作者信息的等价 Java 类。 但是,Axis 仍然将通配符 s:any 映射为 java.lang.Object,而 java.lang.Object 在运行时将包含 DOM 树。 虽然比以前有了很大进步,但客户端仍然直接使用 DOM API。
自定义架构
因为知道通配符槽始终包含 AuthorSet,您可能会尝试修改架构定义以引用 AuthorSet 元素而不是通配符。 但是,通配符实际上是通用 DataSet 架构的占位符(该架构没有什么实际意义,因为通配符受 http://example.org/dataset namespace 的限制)。 换句话说,在运行 WebMethod 时,GetAuthorsAsTypeDataSetResult 元素包含的架构元素紧跟着一个 diffgram 元素,而该 diffgram 元素包含 AuthorSet 元素。
但是由于事先知道该结构,您不必提供运行时的架构或 diffgram 元素;只要返回一个 AuthorSet 元素即可。 实现这一点的方法是定义新的 WebMethod,使其不返回 DataSet 而是返回一个 XmlNode。 然后,如果返回包装类型化 DataSet 对象的 XmlDataDocument,那么只有 AuthorSet 元素返回到客户端,而没有运行时的架构或 diffgram 元素:
[WebMethod]
public XmlNode GetAuthorsAsXml()
{
AuthorSet aus = new AuthorSet();
sqlDataAdapter1.Fill(aus);
return new XmlDataDocument(aus);
}
XmlDataDocument 效率低下,因为它只是粗略地倍增了原始 DataSet 的尺寸。 因为它是暂时的,所以还可以让人接受。
指定该方法返回 XmlNode 也会导致在生成的架构中有一个通配符,但是现在可以利用对 AuthorSet 元素的引用代替通配符,如下所示:
•••
<s:element name="GetAuthorsAsXmlResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1"
name="GetAuthorsAsXmlResult">
<s:complexType>
<s:sequence>
<s:element ref="x:AuthorSet" />
</s:sequence>
</s:complexType>
•••
在关闭该路径后,您将必须确保客户端不会再检索自动生成的 WSDL 文件。 相反,通过使用 WebServiceBindingAttribute(如 Location 属性),或者提供服务器上的静态 WSDL 文件并禁止自动生成 WSDL,您将希望指定自定义的 WSDL 文件位置。 通过将下面的代码添加到 web.config 文件,您可以禁止自动生成 WSDL:
<webServices>
<protocols>
<remove name="Documentation" />
</protocols>
</webServices>
目前 Web 服务仍然可以使用 DataSets,并且其他客户端可以将结果作为对象数组进行处理. 显示在这种情况下 Axis 客户端的样子。
如果使用 DataSets 看起来过于混乱,您始终可以在 Web 服务的界面上避免使用它们或者简单地要求客户端直接处理 XML。 所有这些都取决于您希望其他工具包都能见到什么内容。
最终的解决方案
如果行业定义一个标准的 XML 架构定义来代表数据库结果集,并且所有的工具包都很好地支持它,这个问题最终会得以解决。 然后,工具包可以将其本机的结果集类型映射为标准的架构。 对于其他的动态类型(如集合、哈希表、列表、队列等等),结果是一样的。 针对这些类型来标准化 XML 架构定义,会在极其有用和常用数据结构的更加广阔的范围内提高工具包之间的互操作性。
如果这成为现实,您可以返回通用 DataSet(如第一个 GetAuthors 示例),Axis 工具包会将其映射为 JDBC WebRowSet(或等价的架构)。 然后,Java 开发人员可以将 WebRowSet 绑定到 UI 组件,允许用户修改数据,并将更新后的集合返回到服务器,而只使用最少量的代码。 更新后的集合可以反序列化回服务器上的 DataSet 以便于将更改应用到数据。
如果您想知道为什么要在 Web 服务中使用 DataSet,该解决方案是最有意义的。当您需要以一种通用的方式(通常以网格形式)向用户展示数据,允许用户进行修改,以及将更新应用回数据库时,通常使用 DataSets。 这里说明的技术并不支持该模式(因为您不能通过使用 DOM 树或数组得到数据绑定或 diffgram 支持),所以这更像是非要把方榫头插进圆卯眼。
当然,标准化该行业中的所有内容是说起来容易做起来难。 不用焦急地等待这样的标准成为现实并得到目前的工具包的支持。
.NET Framework 替代方案
您过去可能已经看过贸易展的演示,演示程序将 DataSet 返回到 .NET Framework 客户端,而且它在另一端神奇地变成了 DataSet。 这是因为 .NET Framework 中的 Web 服务基础结构从运行时提供的XML 架构定义中查找名为 IsDataSet 的特定标志。 例如,在 GetAuthorsAsTypedDataSet 示例中,运行时提供的架构中的 AuthorSet 元素声明如下所示:
•••
<xs:element name="AuthorSet" msdata:IsDataSet="true">
•••
对于客户端,IsDataSet 标志是“眨眼”信号,它告诉客户端将 XML 反序列化为 DataSet。 显然,它只针对 .NET Framework 客户端或其他提供该硬编码特殊情况的工具包(据我所知目前没有)有效。 在 中的代码显示 Visual Basic? .NET 客户端是如何调用 GetAuthors 和 GetAuthorsAsTypedDataSet,并且简单地将返回的 DataSet 映射到 DataGrid(在 Windows 窗体上)。
如果数据库结果集的标准化的架构得到每个人的支持,跨所有的工具包和各种各样的数据库 API 就会非常简单。 具有讽刺意味的是,一旦有了这样的东西,如果您的目的是为用户显示数据以进行通用操作,使用 Author 对象的数组实际上是一件更为枯燥的事情。
小结
目前,XML 和 Web 服务提供基本的数据互操作性。 如果您愿意使用 XML API,那么您就可以使用在这个世界上任何 Web 服务。 但是,如果您希望放弃 XML API 并且不再使用它们,那么请注意动态类型(如 DataSet)。 在 Web 服务界面中使用通用 DataSet 将迫使开发人员使用其他工具包来直接处理 XML。 通过将结果作为简单的对象数组进行公开,使用类型化 DataSets 与稍微自定义的 WSDL 定义的结合可以解决这个问题。 但是,最终解决方案是标准化 XML 架构定义,以便表示跨所有工具包支持的数据库结果集。