Использование XSLT Apply-Templates для условного выбора узлов - PullRequest
9 голосов
/ 21 октября 2010

Допустим, у меня есть XML-документ, подобный этому:

<director>
    <play>
        <t>Nutcracker</t>
        <a>Tom Cruise</a>
    </play>
    <play>
        <t>Nutcracker</t>
        <a>Robin Williams</a>
    </play>
    <play>
        <t>Grinch Stole Christmas</t>
        <a>Will Smith</a>
    </play>
    <play>
        <t>Grinch Stole Christmas</t>
        <a>Mel Gibson</a>
    </play>
</director>

Теперь я хочу иметь возможность выбрать все пьесы с Уиллом Смитом в качестве актера и переформатировать его в нечто вроде этого:

<Plays>
    <Play title="Grinch Stole Christmas">
       <star>Will Smith</star>
       <star>Mel Gibson</star>
    </Play>
</Plays>

Я хочу использовать только apply-templates .. Нет xsl: if или для каждого цикла заявление)

Вот что у меня есть:

<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
        <xsl:template match="/director">
                <Plays>
                <xsl:apply-templates select="play"/>
                </Plays>
        </xsl:template>

        <xsl:template match="play[a='Will Smith']">
                <play title="{data(t)[1]}">
                <xsl:apply-templates select="a"/>
                </play>
        </xsl:template>

        <xsl:template match="a">
                <star>
                <xsl:value-of select="."/>
                </star>
        </xsl:template>
</xsl:stylesheet>

По сути, я просто не уверен, как отфильтровать узлы, используя XPath в атрибуте соответствия шаблона. Любая помощь будет отличной!

Ответы [ 3 ]

9 голосов
/ 21 октября 2010

Условие должно быть на xsl: apply-templates вместо xsl: template:

<Plays>
  <xsl:apply-templates select="play[a='Will Smith']">"/>
</Plays>

В вашем решении вы преобразуете ВСЕ узлы.Для игровых узлов, которые соответствуют условию, применяется ваш шаблон.Но для тех, которые не соответствуют условию, вместо этого применяется шаблон по умолчанию («преобразование идентичности»).

В качестве альтернативы, вы можете оставить условие для соответствия xsl: template, но добавить другой шаблон для которые не соответствуют условию, чтобы преобразовать эти в ничто:

    <xsl:template match="play[a='Will Smith']">
      <play title="{data(t)[1]}">
        <xsl:apply-templates select="a"/>
      </play>
    </xsl:template>

    <xsl:template match="play">
    </xsl:template>
5 голосов
/ 21 октября 2010

I. Вероятно, самое эффективное решение XSLT 1.0 :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kWSPlayByTitle" match="play[a='Will Smith']"
  use="t"/>

 <xsl:key name="kActorByTitle" match="a"
  use="../t"/>

 <xsl:template match="/">
  <Plays>
    <xsl:apply-templates select=
    "*/play[generate-id()
           =
            generate-id(key('kWSPlayByTitle',t)[1])
           ]"/>
  </Plays>
 </xsl:template>

 <xsl:template match="play">
  <Play title="{t}">
   <xsl:apply-templates select="key('kActorByTitle',t)"/>
  </Play>
 </xsl:template>

 <xsl:template match="a">
  <star><xsl:value-of select="."/></star>
 </xsl:template>
</xsl:stylesheet>

когда это преобразование применяется к предоставленному документу XML :

<director>
    <play>
        <t>Nutcracker</t>
        <a>Tom Cruise</a>
    </play>
    <play>
        <t>Nutcracker</t>
        <a>Robin Williams</a>
    </play>
    <play>
        <t>Grinch Stole Christmas</t>
        <a>Will Smith</a>
    </play>
    <play>
        <t>Grinch Stole Christmas</t>
        <a>Mel Gibson</a>
    </play>
</director>

желаемый результат получен :

<Plays>
   <Play title="Grinch Stole Christmas">
      <star>Will Smith</star>
      <star>Mel Gibson</star>
   </Play>
</Plays>

Примечание :

  1. Эффективность достигается с помощью клавиш как для всех пьес, в которых участвовал Мелл Гибсон, так и для всех актеров, которые принимали участие в данной (названной) пьесе.

  2. Даже если пьеса с Мелом Гибсоном была указана более одного раза (возможно, из-за случайной ошибки ...), она будет указана только один раз в результате .

II. Простое и эффективное решение XSLT 2.0 :

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/*">
  <Plays>
    <xsl:for-each-group select="play[a='Mel Gibson']"
          group-by="t">
      <xsl:apply-templates select="."/>
    </xsl:for-each-group>
  </Plays>
 </xsl:template>

 <xsl:template match="play">
  <Play title="{t}">
   <xsl:for-each-group select="../play[t = current()/t]/a"
        group-by=".">
     <xsl:apply-templates select="."/>
   </xsl:for-each-group>
  </Play>
 </xsl:template>

 <xsl:template match="a">
  <star>
    <xsl:value-of select="."/>
  </star>
 </xsl:template>
</xsl:stylesheet>
1 голос
/ 21 октября 2010

Эта таблица стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="kActorByTitle" match="a" use="../t"/>
    <xsl:param name="pActor" select="'Will Smith'"/>
    <xsl:template match="/">
        <Plays>
            <xsl:apply-templates select="*/play[a=$pActor]"/>
        </Plays>
    </xsl:template>
    <xsl:template match="play">
        <Play title="{t}">
            <xsl:apply-templates select="key('kActorByTitle',t)"/>
        </Play>
    </xsl:template>
    <xsl:template match="a">
        <star>
            <xsl:value-of select="."/>
        </star>
    </xsl:template>
</xsl:stylesheet>

Выход:

<Plays>
    <Play title="Grinch Stole Christmas">
        <star>Will Smith</star>
        <star>Mel Gibson</star>
    </Play>
</Plays>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...