挑战 XSLT 最擅长的 XML 文档转换
XML 是描述数据的最佳语言,XML 是一个庞大的家族,因此很多人都极力的发掘 XML 的潜力,欲将其用至极致。
XSLT 的出现无疑大大加重了这样的倾向,因为 XSLT 中支持了众多的编程元素,所以对于 XML 文档的转换无疑采用 XSLT 是极为方便的。甚至有人采用 XSLT 作为模板来编写代码生成器。
本文将对这样对于 XML 的极致使用目标提出质疑,认为过分极致的使用目标就是“误用”,会使我们减少对于更适宜方案的选择。本文认为“物尽其用”不如“物用其宜”。
本文从一个简单的 XML 文档转换问题出发,给出采用传统的 XSLT 转换代码,并且针对这样的转换方案做出改进,采用一种新的编程语言 Nuva 语言(也可以采用其他编程语言)编写转换程序。两厢对比,相信读者不难看出,采用编程语言比 XSLT 这样的转换语言具有入门门槛低、程序更短、更易读以及更容易维护等众多的优点。
考虑如下的源 XML 文档,该文档是 Microsoft .NET 的 DataSet 对于 XML 的存档文件:
==【DotNetDataSet-Sample.xml】==
<?xml version="1.0" encoding="UTF-8"?> <Schema1> <Table1> <Field11>A1</Field11> <Field12>A2</Field12> </Table1> <Table1> <Field11>B1</Field11> <Field12>B2</Field12> </Table1> <Table1> <Field11>C1</Field11> <Field12>C2</Field12> </Table1> <Table2> <Field21>D1</Field21> <Field22>D2</Field22> <Field23>D3</Field23> </Table2> <Table2> <Field21>E1</Field21> <Field22>E2</Field22> <Field23>E3</Field23> </Table2> </Schema1>
现在欲将其转换成如下的 XML 文档,该文档是 Macrobject XObject 的 标准 XML 存档文件:
==【ObjectSpace-Sample.xml】==
<?xml version="1.0" encoding="UTF-8"?> <ObjectSpace Name="Schema1"> <ObjectSet Name="Table1"> <Object Field11="A1" Field12="A2" /> <Object Field11="B1" Field12="B2" /> <Object Field11="C1" Field12="C2" /> </ObjectSet> <ObjectSet Name="Table2"> <Object Field21="D1" Field22="D2" Field23="D3" /> <Object Field21="E1" Field22="E2" Field23="E3" /> </ObjectSet> </ObjectSpace>
源 XML 文档是一种“长”结构,所有的字段都用单独的一个节点来表示,表的每一行分别表示,无论表是否相同,都相互没有关系,看起来比较费劲。另外其缺点是上下可能比较“长”。
目标 XML 文档采用一种“宽”结构,字段都放在所属行(Object)的属性中,每一个表(ObjectSet)的所有行放在同一个节点下,所以很容易分表来看。缺点是左右可能比较“宽”。
下面采用传统的 XSLT 来编写转换程序,如下:
==【DotNetDataSet2ObjectSpace.xslt】==
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/02/xpath-functions"
xmlns:xdt="http://www.w3.org/2005/02/xpath-datatypes">
<xsl:template match="/">
<xsl:apply-templates select="child::node()"/>
</xsl:template>
<xsl:template match="child::node()">
<xsl:element name="ObjectSpace">
<xsl:attribute name="Name">
<xsl:value-of select="fn:node-name(.)"/>
</xsl:attribute>
<xsl:variable name="ChildNodes" select="./*" />
<xsl:variable name="NodeNames">
<xsl:element name="Node">
<xsl:for-each select="$ChildNodes">
<xsl:attribute name="{fn:node-name(.)}">
<xsl:value-of select="'true'" />
</xsl:attribute>
</xsl:for-each>
</xsl:element>
</xsl:variable>
<xsl:variable name="DistinctNodes">
<xsl:for-each select="$NodeNames/@*">
<xsl:element name="{fn:name(.)}">
</xsl:element>
</xsl:for-each>
</xsl:variable>
<xsl:for-each select="$DistinctNodes">
<xsl:element name="ObjectSet">
<xsl:attribute name="Name">
<xsl:value-of select="fn:node-name(.)" />
</xsl:attribute>
<xsl:variable name="Name" select="fn:node-name(.)" />
<xsl:for-each select="$ChildNodes [fn:node-name(.) = $Name]">
<xsl:element name="Object">
<xsl:for-each select="./*">
<xsl:attribute name="{fn:node-name(.)}">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:for-each>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
简单看一下,用 XSLT 写的转换程序比较复杂,而且也用了一些技巧,学起来比较费劲,老实说,我现在已经无法维护它了,因为时间一长,我已经不怎么看得懂了。
后来,我转换了一下思维,用 Nuva 语言重写了一次,结果连我自己也吃了一惊,非常的简洁,如下:
<..========================================================
== ==
== Macrobject Nuva Samples ==
== ==
== Copyright (c) 2004-2006 Macrobject Software ==
== ==
== ALL RIGHTS RESERVED ==
== ==
== http://www.macrobject.com ==
== ==
========================================================..>
<.
var sourceFile = 'DotNetDataSet-Sample.xml'
var source = System.Xml.DocumentsourceFile var target = System.Xml.Document'ObjectSpace' target.Root.Props.Name = source.Root.Name
foreachsTable = source.Root.Nodes var tObjSet = FindOrCreateObjectSettarget.Root, sTable.Name var tObj = tObjSet.Nodes.Add'Object' foreachsField = sTable.Nodes tObj.PropssField.Name = sField.Value
end foreach
function FindOrCreateObjectSettheParent, theName Result = theParent.FindByProp'Name', theName if Result = nil Result = theParent.Nodes.Add'ObjectSet' Result.Props.Name = theName
end if
end function
end foreach
? target.ToString
.>
<..
【简介】
本例是一个 XML 文档转换程序,本例的源 XML 文档是 Microsoft .NET 的 DataSet 存档为 XML 的文件,本例的目标 XML 文档是 Macrobject XObject 的 XML 存档文件。
Microsoft .NET DataSet 信息请参考微软网站的相关内容:www.microsoft.com
Macrobject XObject XML 是 Macrobject 所有 O/R Mapping 的标准存档格式,这个 XML 文件可以模拟一个简单的关系数据库,具体信息请参考 www.macrobject.com 的有关内容。
本例演示了 Nuva 语言操纵 XML 文档的便利性,甚至超过了 XML 标准转换语言 XSLT。
【看点】
1、本例演示了 Nuva 语言在 XML 的应用
Nuva 语言提供了一个 XML 库,其中包括 XmlDocument、XmlNode、XmlNodeList 类:
XmlDocument 对象通过 System.XML.Document([名称]) 创建,其中可以包含一个名称参数,如果该名称是一个文件,则从文件读取,否则返回的 XmlDocument 的 Root 节点即可以此名称来命名。
XmlDocument 有一些属性和方法,具体可以参见<<Nuva API>>,本例用到了一下的属性和方法:
XmlDocument.Root:返回根节点(XmlNode 对象)
XmlDocument.ToString:返回 Xml 的字符串
XmlNode 有以下属性和方法在本例中使用:
XmlNode.Props:返回一个属性集,后面可以直接加上 .<PropName> 或者 [<索引器>],可以为此属性取值赋值
XmlNode.Nodes:返回一个节点集(XmlNodeList 对象)
XmlNode.FindByProp(theName, theValue, [inSub]):返回一个子节点,最后一个可选参数规定是否深层次递归查找所有子节点
本例用到了 XmlNodeList 的一个方法:
XmNodeList.Add(theName, [theValue]):返回一个新子节点,最后一个可选参数指定其 Value
2、本例中对于 ObjectSet 节点的创建调用了一个函数 FindOrCreateObjectSet,目的是对于相同的表名必须只能创建一次,从而将相同的表(TableRow,Object)放在同一个 ObjectSet 节点下。
【扩展】
本例可以进一步扩展以增强其实用性,比如可以修改以用于更多类型的 XML 文档转换。
..>
本例运行结果如下:
<?xml version="1.0"?>
<ObjectSpace Name="Schema1">
<ObjectSet Name="Table1">
<Object Field11="A1" Field12="A2"/>
<Object Field11="B1" Field12="B2"/>
<Object Field11="C1" Field12="C2"/>
</ObjectSet>
<ObjectSet Name="Table2">
<Object Field21="D1" Field22="D2" Field23="D3"/>
<Object Field21="E1" Field22="E2" Field23="E3"/>
</ObjectSet>
</ObjectSpace>
posted on 2006-09-08 09:22 Wisdom-zh 阅读(2628) 评论(19) 编辑 收藏 举报