级别: 初级 高 爽 (gaos@cn.ibm.com), 软件工程师, IBM
2008 年 7 月 01 日
数据提供者和消费者是使用 WebSphere Portlet Factory(以下简称 WPF)开发应用的核心模式,符合 Service-Oriented Architecture(SOA)的理念。利用它可以很好地屏蔽后台系统的复杂性和多样性,实现数据层与展现层的分离,提供给开发人员一个松耦合的应用架构。支撑这一模式的核心技术是 XML 。 XML 作为数据提供者和消费者之间统一的数据流形式,Schema 负责描述 XML 的数据结构。
本文旨在分析如何利用 WPF 提供的处理 XML 的手段,包括使用构建器(Builder)和内置 API 两种方式。为开发人员提供帮助和指导。
使用构建器(Builder)处理 XML
模式(Schema)构建器
此构建器用于定义 XML Schema,即 XML 文档的数据结构。同时,提供了验证 XML 的机制。利用模式构建器可以通过两种方式定义 Schema:
- 直接以 URL 或文件两种形式引用已经定义完成的 Schema 。例如:http://www.myschemas.org/schemas/schema1.xsd或 file://c:\myschema\schema1.xsd
- 在构建起的 模式源 输入框提供描述 Schema 的 XML 文档。
简单模式生成器(Simple Schema Generator)构建器
此构建器可以根据一个已经在模型(Model)中存在的 XML 变量自动生成 Schema 。使用这种方法,可以简化 Schema 的定义。
转换(Transform)构建器
此构建器能够实现将一个模式类型(定义了 Schema)XML 变量的内容复制或合并到另一个模式类型的变量中。其优点在于不需要编写任何 XSLT 或 IXml(WPF 中提供的处理 XML 的 API,会在 使用 API 处理 XML 部分介绍)代码。借助于 转换构建器的功能,可以帮助我们快速实现复合应用(Composite application),将来源于不同系统的数据合并到一个 XML 变量中,从而通过统一、标准的界面展现给用户。
转换 - 排序(Transform-Sort)构建器
此构建器可以对 XML 变量中的元素进行排序。排序后的结果可以直接保存到原始变量或其它 XML 变量。
转换 - 过滤(Transform-Filter)构建器
此构建器可以根据设定的条件过滤 XML 变量中的元素。过滤后的结果可以直接保存到原始变量或其它 XML 变量。
样例介绍
下面将通过一个例子,帮助大家深入理解以上介绍的构建器如何处理 XML 。
应用场景描述:我们开发一个待办信息查询的应用,使用户在一个界面中,可以获知需要他处理的所有工作。在实际的应用环境里,待办信息往往分散在不同的应用系统中,比如:数据库、Domino、ERP 等等。用户需要不停地在系统间进行切换以获取信息。在我们开发的应用中,将采用复合应用的方式。用户在一个标准化的统一界面中,可以快速访问到来自多个系统同一性质的数据和信息。为了便于代码共享,在我们的例子中使用 XML 和 Excel 作为后台数据源。
-
在 WPF 项目(已有或新建)中创建一个空的模型。然后,添加一个类型为变量的构建器。在构建器定义界面中,选择类型为 XML ;将我们事先准备好的 XML 格式的待办信息文档拷贝到初始值输入框中。
图 1. 定义存储 XML 格式待办信息的变量
- 添加类型为 简单模式生成器 的构建器。样本数据选择之前定义的 XML 变量。
图 2. 生成待办信息 XML 的 Schema
- 切换到模型的 Web 应用程序树 视图。在 Schema 部分,我们可以发现构建器已经根据 XML 变量生成了相应的 Schema 。
图 3. Schema 的 XML 描述文档
- 把变量的类型由最初的 XML 改为 Schema 所定义的模式类型。
图 4. 修改变量类型
- 在模型中添加为Excel 导入的构建器。把存储在 Excel 表格中的待办信息数据导入模型。Excel 导入构建器会根据提供的数据自动生成 Schema 。
图 5. 在导入文件输入框提供 Excel 文件所在的路径
-
自此,XML 和 Excel 中的数据已经被先后注入模型。接下来,为了创建一个新的 XML 变量用于存储从两个数据源合并后的结果,我们首先需要定义一个描述统一代办信息的 Schema 。在模型中添加类型为模式的构建器。在模式源输入框中提供描述 Schema 的 XML 文档。建议使用专业创建 XML Schema 的工具,如 XMLSpy 。然后把创建好 Schema 文档拷贝到输入框中。
图 6. 定义描述统一待办信息的 Schema
- 添加一个新的变量,类型为统一待办信息 Schema 。
图 7. 变量用于存储合并后的待办信息
- 下面的部分是构建此应用的核心部分,涉及到如何对数据进行合并的操作。在模型中添加一个类型为转换的构建器。此构建器负责将源模式类型的 XML 变量(全部或部分)复制到目标模式类型的 XML 变量。第一步先把源数据为 XML 的待办信息拷贝到之前定义的统一待办信息 XML 变量。开始拷贝之前,可以指定是否清空目标变量。
图 8. XML 变量复制
下图表示了目标模式与源模式节点间的映射关系。在统一待办信息 Schema 中未定义 category 元素,故存储在 XML 数据中的 category 信息会被放弃。
图 9. 映射关系
- 接下来,拷贝存储在 Excel 表格中的数据。方法同上。
图 10. 注意节点之间的映射关系
- 在模型中添加一个类型为 操作列表 的构建器,用于执行数据拷贝的操作。方法由转换构建器产生,名称形式为:<TransformName>CallCopy 。
图 11. 执行拷贝操作
- 通过两次调用 转换 构建器,之前分散存储在 XML 和 Excel 中的数据,已经合并到一个 XML 变量中。调用 视图和表单 构建器,生成显示待办信息列表的页面。
图 12. 负责展现的构建器
- 运行模型。我们可以看到存储在多个系统中的待办信息实现了统一展现。
图 13. 待办信息列表
-
为了方便用户查看,可以按照优先级对待办信息排序。在模型中添加变换 - 排序构建器。指定待排序的 XML 变量,设定 priority 作为排序条件。比较类型分为四种:区分大小写的字符串、不区分大小写、数字和日期。
图 14. 按照优先级对待办信息排序
- 修改 操作列表 构建器,加入执行排序的方法。方法由变换 - 排序构建器产生,名称形式为:<builderName>CallSort
图 15. 执行排序操作
- 运行模型。可以看到待办信息按照优先级别进行了排序。
图 16. 按优先级排序后的待办信息列表
- 同时,我们也可以对待办信息进行过滤。在模型中添加类型为变换 - 过滤的构建器,提供待过滤的 XML 变量,指定过滤条件:状态为进行中的待办。
图 17. 筛选状态为进行中的待办信息
- 修改 操作列表 构建器,加入执行过滤操作的方法。方法由变换 - 过滤构建器产生,名称形式为:<builderName>CallFilter
图 18. 执行过滤操作
- 运行模型。界面只显示满足条件的数据。
图 19. 显示状态为进行中的待办信息

 |

|
使用 API 处理 XML
除了以上部分介绍的标准化构建器,WPF 同时提供处理 XML 的 API 。适用于在使用构建器无法满足应用要求的情况,比如:当源数据或目标数据的 XML Schema 结构比较复杂,差异比较大时,直接用转换构建器的方法进行数据复制比较困难,无法指定节点之间的映射关系。 API 的主要的接口是 com.bowstreet.util.IXml,采用标准的 DOM(Document Object Model)方式处理 XML 。可以实现插入 XML 元素(Element)、获取文本(Text)、设置属性(Attribute)等常规的 XML 操作。
样例介绍
应用场景:某 ERP 系统提供 XML 文档,其中包含财务系统中的数据。需要借助图形或仪表盘等形式在门户中进行展现。
清单 1 中的 XML 用于描述财务范畴的台帐(Voucher)信息;清单 2 中的 XML 描述人员信息。在台帐 XML 中引用了人员代码,通过personnel_id属性与人员 XML 进行关联。
清单 1. 台帐信息 XML 片段
<vouchers>
…
<voucher id="00001">
<voucher_head>
<company> 技术中心 </company>
<voucher_type> 记 </voucher_type>
<fiscal_year>2007</fiscal_year>
<date>2007-01-25</date>
<cashier> 李四 </cashier>
</voucher_head>
<voucher_body>
<entry>
<abstract> 购买保险柜 </abstract>
<natural_debit_currency>15000</natural_debit_currency>
<bill_type> 新增资产 </bill_type>
<auxiliary_accounting>
<item name="dept_id"> 科技信息室 </item>
<item name="personnel_id">62101500</item>
</auxiliary_accounting>
</entry>
</voucher_body>
<voucher>
…
<vouchers>
|
清单 2. 人员信息 XML 片段
<persons>
…
<person>
<code>62101500</code>
<name> 张三 </name>
<rsex>2</rsex>
<rpersontype>10</rpersontype>
<bpsnperson>1</bpsnperson>
<cdept_num>12</cdept_num>
<dpvaliddate>2007-8-11</dpvaliddate>
</person>
…
</persons>
解决之道
|
分析过应用的需求后发现,我们面临的主要问题是如何对 ERP 提供的 XML 进行处理,包括数据统计和数据之间的关联查询。利用 XPath(XML Path Language)中的 SUM 函数可以实现数据汇总,也可以对 XML 中的节点进行定位和查找。但问题在于,原始的 XML 数据格式(Schema)比较复杂,信息分散在多个 XML 文档中。直接利用原始数据,实现统计、查询这样的操作比较繁琐。另外,我们也需要把一个统一的数据格式提供给前端的展现组件,在进行数据展示时可以更加灵活。在这种情况下,我们设计了一个描述统一台帐信息的 Schema 。
图 20. Schema 逻辑视图
我们可以看到,Schema 中包括了用来统计的指标项,如部门(dept_id)、人员(personnel_id)、日期(date)等。这样我们在做统计时,XPath 表达式会更加简单、清晰。同时也包括之前需要通过跨多个 XML 文档进行关联的项,personnel_id 和 personnel_name 。这样的设计可以简化查询操作,在实现前端展现的时候也更加统一、灵活。
Schema 定义完成后,我们需要考虑把原始的 XML 文档按照 Schema 定义的格式进行转换。在前一章节,我们已经介绍过利用转换构建器可以实现数据复制和合并的方法。但是,转换构建器适用于源模式与目标模式的数据结构差异性不大的情况。一旦原始的 XML 数据结构比较复杂,很难直接设定目标模式与源模式节点间的映射关系。这样,我们需要借助编码,通过程序进行数据的转换。 WPF 提供类型为链接的 Java 对象(Linked Java Object)构建器,使我们可以在模型中调用 Java 代码。
实现步骤和方法
首先将 XML 文档导入 WPF 模型
-
在 WPF(已有或新建)项目中,新建一个空的模型。
-
将台帐 XML 和人员 XML 文件添加到项目中,如下图:
图 21. 台帐 XML 和人员 XML 在项目中的路径
-
在模型中添加类型为导入到 XML构建器。在文件路径输入框中提供台帐 XML 文件所处的路径。
图 22. 引用台帐 XML
-
添加类型为简单模式生成器的构建器,生成台帐 XML 的 Schema 。
图 23. 生成台帐 XML Schema
-
导入人员 XML 与台帐类似,不在此赘述。
-
在模型中添加类型为 模式 的构建器,定义 统一台帐信息的 Schema。
图 24. Schema 定义
-
创建变量,数据类型为统一台帐信息 Schema
图 25. 用于存储整合后的台帐信息
-
接下来,实现一个 Java 类,负责把原始 ERP XML 文档中的数据整合到新的 Schema 类型的 XML 变量。 Java 类(TransformVoucherXml.java)创建到 WebContent/WEB-INF/work/source 路径下,如下图:
图 26. Java 类所在的路径
清单 3. 把 ERP 提供的 XML 数据整合到新 Schema 类型的 XML 变量
public void transformXml(WebAppAccess webAppAccess)
{
// 统一台帐信息 Schema 类型的 XML 变量
IXml voucherData = webAppAccess.getVariables().
getXmlElement("voucherData", "vouchers");
// ERP 系统导出的台帐 (Voucher)XML 数据
IXml voucherXml = webAppAccess.getVariables().
getXmlElement("voucherXml", "vouchers");
// ERP 系统导出的人员 XML 数据
IXml employeeXml = webAppAccess.getVariables().
getXmlElement("employeeXml", "persons");
int voucherSize = voucherXml.getChildren().size();
List oldVoucherList = voucherXml.getChildren();
IXml tVoucherNode = null;
IXml tOldVoucherNode = null;
String personnelId = "";
String personnelName = "";
// 把 ERP 提供的 XML 数据复制到新 Schema 类型的 XML 变量
for (int i = 0; i < voucherSize; i++)
{
tOldVoucherNode = (IXml) oldVoucherList.get(i);
// 添加 voucher 元素
tVoucherNode = voucherData.addChildElement("voucher");
// 添加 company 元素
tVoucherNode.addChildWithText("company",
tOldVoucherNode.findElement("voucher/voucher_head/company").getText());
// 添加 dep_id 元素
tVoucherNode.addChildWithText("dept_id", tOldVoucherNode.findElement(
"voucher/voucher_body/entry/auxiliary_accounting/item[@name=dept_id]/")
.getText());
// 添加 fiscal_year 元素
tVoucherNode.addChildWithText("fiscal_year",
tOldVoucherNode.findElement("voucher/voucher_head/fiscal_year").getText());
// 添加 date 元素
tVoucherNode.addChildWithText("date",
tOldVoucherNode.findElement("voucher/voucher_head/date").getText());
// 添加 abstract 元素
tVoucherNode.addChildWithText("abstract",
tOldVoucherNode.findElement("voucher/voucher_body/entry/abstract").getText());
// 添加 natural_debit_currency 元素
tVoucherNode.addChildWithText("natural_debit_currency",
tOldVoucherNode.findElement("voucher/voucher_body/entry/natural_debit_currency")
.getText());
// 添加 bill_type 元素
tVoucherNode.addChildWithText("bill_type",
tOldVoucherNode.findElement("voucher/voucher_body/entry/bill_type").getText());
// 添加 personnel_id 元素
personnelId = tOldVoucherNode.findElement(
"voucher/voucher_body/entry/auxiliary_accounting/item[@name=personnel_id]")
.getText();
tVoucherNode.addChildWithText("personnel_id", personnelId);
// 通过 personnel_id 在人员 XML 中查找 personnel_name
personnelName = employeeXml.findElement(
"v_aa_hr_hi_person/[code=" + personnelId + "]/../name").getText();
// 添加 personnel_name 元素
tVoucherNode.addChildWithText("personnel_name", personnelName);
}
}
|
通过清单 3 中的代码,数据已经按照新 Schema 定义的数据结构整合到一个 XML 变量中。接下来,我们会依据一些指标对数据进行统计,如:部门、日期和人员等。 IXml 接口并没有提供相应的方法完成此类功能。我们需要借助一些时下流行的 XML 处理器,如:Xalan(http://xalan.apache.org)或 XMLBeans(http://xmlbeans.apache.org)去处理。在此,不做过多介绍,请参考相应的资源。
- 在模型中添加类型为 链接的 Java 对象 构建器。
图 27. 把 Java 类引入模型
图 28. 在模型中可以调用 Java 类的公用方法
-
最后,我们便可以利用像视图和表单或Web 图表等展现端构建器显示统计结果。
图 29. 显示统计结果

 |

|
结束语
使用 WPF 开发的应用,XML 作为统一的数据格式,真正做到了数据格式的系统无关性。借助这一特性,使之成为一款名副其实的符合 SOA 规范的开发工具。在多数情况下,直接利用构建器的功能处理 XML 便可以满足应用的要求,而无需进行任何编码。同时,我们也可以利用系统提供的 API 去应对一些特殊情况。
下载 | 描述 | 名字 | 大小 | 下载方法 |
|---|
| 本文样例代码。 | HandleXMLDemo.zip | 8 KB | HTTP |
|---|
参考资料 学习
讨论
关于作者  | |  | 高爽,IBM(中国)软件部工程师,负责合作伙伴技术支持。领域涉及 WebSphere Portal 及相关解决方案,具备丰富的门户应用开发经验。 |
对本文的评价
|