XSLT的新手,但是从这里的帖子中学到了很多东西。但是,我遇到了一个问题。
我正在使用XSLT为设备安装创建报告。输入XML如下所示:
<DeviceTypes>
<DeviceInfo Model="51473">
<Channels>
<ChannelInfo ChannelId="1" IsImplemented="false" SampRateHardware="448" />
<ChannelInfo ChannelId="2" IsImplemented="true" SampRateHardware="224" />
</Channels>
</DeviceInfo>
<DeviceInfo Model="51474">
<Channels>
<ChannelInfo ChannelId="1" IsImplemented="true" SampRateHardware="448" />
<ChannelInfo ChannelId="2" IsImplemented="true" SampRateHardware="224" />
</Channels>
</DeviceInfo>
</DeviceTypes>
<Installation>
<InstalledDevice Serial="597657" Model="51473">
<Channels>
<InstalledChannel ChannelId="1" Name="foo" />
<InstalledChannel ChannelId="2" Name="bar" />
</Channels>
</InstalledDevice>
</Installation>
如果相应的ChannelInfo的“IsImplemented”设置为true,我想只处理InstallChannel节点。通过“对应”我的意思是我在父节点下寻找具有相同ChannelId和相同Model的ChannelInfo。请注意,具有相同ChannelId的通道可能具有不同的IsImplemented值,具体取决于它们所处的设备。
我一直在使用key()函数来成功查找,但这个嵌套查找让我感到难过。
谢谢,
-Mat
答案 0 :(得分:1)
以下是使用密钥的简短(无条件,无变量,无xsl:for-each
)解决方案:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kCI-ByIdImpl" match="ChannelInfo"
use="concat(@ChannelId,
'+', @IsImplemented,
'+', ../../@Model)"/>
<xsl:template match="/*">
<xsl:copy-of select=
"Installation/*/*
/InstalledChannel
[key('kCI-ByIdImpl',
concat(@ChannelId, '+true',
'+', ../../@Model)
)
]"/>
</xsl:template>
</xsl:stylesheet>
将此转换应用于提供的XML片段(包装到单个顶部元素中,以构成格式良好的XML文档):
<t>
<DeviceTypes>
<DeviceInfo Model="51473">
<Channels>
<ChannelInfo ChannelId="1" IsImplemented="false" SampRateHardware="448" />
<ChannelInfo ChannelId="2" IsImplemented="true" SampRateHardware="224" />
</Channels>
</DeviceInfo>
<DeviceInfo Model="51474">
<Channels>
<ChannelInfo ChannelId="1" IsImplemented="true" SampRateHardware="448" />
<ChannelInfo ChannelId="2" IsImplemented="true" SampRateHardware="224" />
</Channels>
</DeviceInfo>
</DeviceTypes>
<Installation>
<InstalledDevice Serial="597657" Model="51473">
<Channels>
<InstalledChannel ChannelId="1" Name="foo" />
<InstalledChannel ChannelId="2" Name="bar" />
</Channels>
</InstalledDevice>
</Installation>
</t>
只处理了需要的InstalledChannel
元素(在这种情况下只是复制到输出中):
<InstalledChannel ChannelId="2" Name="bar"/>
解释:合理使用复合键。
答案 1 :(得分:0)
这样的事情应该有效。
/Installation/InstalledDevice/Channels/InstalledChannel/[count(/DeviceTypes/DeviceInfo/Channels/ChannelInfo[@ChannelId = @ChannelId and @IsImplemented = 'true') = 1]
答案 2 :(得分:0)
我相信使用模板可以提高可读性/可扩展性:关键是使用变量来为InstalledChannel的xpath中的ChannelInfo节点引用Model和ChannelId,所以从InstalledDevice开始,并继续沿着层面工作
<xsl:apply-templates select="//InstalledDevice"/>
<xsl:template match="//InstalledDevice">
<xsl:variable name="model">
<xsl:value-of select="@Model"/>
</xsl:variable>
<xsl:for-each select="Channels/InstalledChannel">
<xsl:variable name="channelId">
<xsl:value-of select="@ChannelId"/>
</xsl:variable>
<xsl:if test="//DeviceInfo[@Model=$model]/Channels/ChannelInfo[@ChannelId=$channelId and @IsImplemented='true']">
Processing Goes Here
</xsl:if>
</xsl:for-each>
</xsl:template>
为了保存模型变量的上下文,我将InstalledChannel处理移动到同一个模板中,并添加了for-each。这样,可以单独检查每个InstalledChannel实例是否需要处理,并相应地进行处理。