XSLT вложенный поиск - PullRequest
       1

XSLT вложенный поиск

2 голосов
/ 02 апреля 2012

Новичок в 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>

Я хочу обрабатывать узел InstallChannel только в том случае, если для соответствующего ChannelInfo для IsImplemented установлено значение true. Под «соответствующим» я имею в виду, что я ищу ChannelInfo с тем же ChannelId и той же моделью под родительским узлом. Обратите внимание, что каналы с одинаковым ChannelId могут иметь разные значения IsImplemented в зависимости от того, под каким устройством они находятся.

Я использовал функцию key () для успешного поиска, но этот вложенный поиск поставил меня в тупик.

Спасибо

-Mat

Ответы [ 3 ]

1 голос
/ 03 апреля 2012

Вот краткое и простое (без условий, без переменных, нет 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>

только 1016 * требуемый элемент обрабатывается (в этомрегистр просто копируется на выход):

<InstalledChannel ChannelId="2" Name="bar"/>

Пояснение : правильное использование составного ключа.

0 голосов
/ 02 апреля 2012

Я считаю, что использование шаблонов обеспечивает лучшую читаемость / расширяемость: ключ использует переменную, чтобы иметь возможность ссылаться как на Model, так и на ChannelId для узла ChannelInfo в xpath для InstalledChannel, поэтому начните с 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 голосов
/ 02 апреля 2012

Как-то так должно работать.

/Installation/InstalledDevice/Channels/InstalledChannel/[count(/DeviceTypes/DeviceInfo/Channels/ChannelInfo[@ChannelId = @ChannelId and @IsImplemented = 'true') = 1]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...