我开发了一个(半)身份转换,我需要从中筛选出未使用的元素。
XML源包含2001个 “区域”。不多也不少。
它还包含任意数量的设备,这些设备被放置在这些区域中。一个具体的例子源XML包含8800个这样的设备,同一个区域中可以放置多个设备。
Zone 0是一个 “null zone”,这意味着放置在这个区域的设备目前是未分配的,这意味着实际区域的数量是2000。
简化后的源XML。
<configuration>
<zones>
<zone id="0">
...
<zone id="2000"/>
</zones>
<devices>
<device addr="1">
<zone>1</zone>
</device>
...
<device addr="8800">
<zone>1</zone>
</device>
</devices>
</configuration>
我们的问题是,在2000个可用的区域中,大多数情况下,只有大约200个区域包含了一个或多个设备,我需要把未使用的区域删除。这其中有很多原因,但这些原因只会影响到我们手头的问题,所以如果你不介意的话,我就不在这里详细说明了。
我目前已经解决了这个问题,像这样。
<xsl:for-each select="zones/zone[@id > 0]">
<xsl:when test="/configuration/devices/device[zone=current()/@id]">
<xsl:call-template name="Zone"/>
</xsl:when>
</xsl:for-each>
但在一些大型项目上,这个转换要花上好几年的时间,因为在伪代码中,这个转换是这样的:
for each <zone> in <zones>
find any <device> in <devices> with reference to <zone>
if found
apply zone template
endif
endfor
有2000个区域需要迭代–而每次迭代都会触发8800次搜索,寻找一个合格的设备–你可以想象这需要很长的时间。
而且问题更加复杂。libxslt 没有提供进度报告的API。这意味着在很长一段时间内,我们的应用程序在导入和转换客户XML的过程中会出现冻结。
我确实可以选择无条件地写入每个区域,并在应用程序从我们的(输出)XML中启动时,删除或忽略任何没有放置设备的区域。
这样做的缺点是,我的输出XML中包含了很多没有被引用的区域.这使得我们很难整合我们的配置中的内容以及应用程序实际使用的部分。
我想问的是。
我是否有其他的选择来确保输出的XML只包含使用过的区域?
我并不反对进行后续的XSLT转换.例如,我在想,也许可以(? used="false"
给每个 <Zone>
元素在我的输出.然后,当我走过设备,我找到相关的区域在我的输出XML(提供它是分配的区域是非零),并改变这个 used="true"
.然后再进行快速的二次转换,以删除所有带有 used="false"
.
但是,我可以在XSLT转换过程中引用我自己的输出元素并改变其内容吗?
解决方案:
你说你有一种身份转换,所以我会以此为出发点,加上一个键。
<xsl:key name="zone-ref" match="device" use="zone"/>
和一个空模板
<xsl:template match="zones/zone[not(key('zone-ref', @id))]"/>
防止未引用的 zone
的复制。
或者,如果还有其他条件,那么,如
<xsl:template match="zones/zone[@id > 0 and not(key('zone-ref', @id))]"/>