2012-04-02 77 views
2

XSLT新手,但在这里的帖子中学到了很多东西。但是,我陷入了一个问题。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和父节点下相同的模型的ChannelInfo。请注意,具有相同ChannelId的通道可能具有不同的IsImplemented值,具体取决于它们所处的设备。

我一直在使用和key()函数来成功查找,但这种嵌套查找让我难堪。

感谢,

-Mat

回答

0

我相信使用模板实现更好的可读性/扩展性:关键是使用变量能够引用两个型号和渠道ID为在XPath的InstalledChannel的ChannelInfo节点,所以开始与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实例都可以单独检查是否需要处理,并据此处理。

+0

谢谢。我认为你很接近。同样,诀窍是可能有多个ChannelInfo在不同的DeviceInfo下具有相同的ChannelId,并且它们不一定都具有相同的IsImplemented。我会更新示例来说明这一点。 – mek363 2012-04-02 19:13:31

+0

没问题,相同的一般概念仍然适用,你只需要在树上开一个更高的点,并且你的xpath变得有点复杂。 (我更新了我的答案)。 – javram 2012-04-02 19:29:03

+0

我认为现在的问题是,您的变量“channelId”将抓取第一个InstalledChannel,但可能会有多个。我试图保持我的例子简单。我会再次更新。谢谢。 – mek363 2012-04-02 21:02:33

0

这样的事情应该工作。

/Installation/InstalledDevice/Channels/InstalledChannel/[count(/DeviceTypes/DeviceInfo/Channels/ChannelInfo[@ChannelId = @ChannelId and @IsImplemented = 'true') = 1] 
+0

谢谢。我不认为这会起作用,因为在不同的DeviceInfo下可能有多个具有相同ChannelId的ChannelInfo,并且它们不一定都具有相同的IsImplemented。 – mek363 2012-04-02 19:08:17

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"/> 

说明:适当使用复合密钥。