XSLT嵌套查找

时间:2012-04-02 18:44:52

标签: xslt lookup

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

3 个答案:

答案 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实例是否需要处理,并相应地进行处理。