2010-10-20 2 views
8

Допустим, у меня есть XML-документ, как это:Использование XSLT Apply-шаблоны условно выберите узлы

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

Я только хочу, чтобы использовать применяемые-шаблоны .. Нет XSL: если или для каждой петли (я умудрилась этот пример в качестве более простой версии того, что я делаю, так что вы можете мне помочь понять, как использовать xpath в заявлении соответствия)

Вот то, что я до сих пор:

<?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 в атрибуте соответствия шаблона. Любая помощь будет замечательной!

+0

Что делает то, что вы сделали до сих пор? –

+0

Решение ниже было решено ... Спасибо за комментарий как минимум :) – Msencenb

+0

Хороший вопрос, +1. См. Мой ответ на два эффективных решения :) –

ответ

7

условия должно быть на XSL: Нанести-шаблоны вместо XSL: шаблон:

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

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

В качестве альтернативы, вы можете сохранить состояние на XSL: матч шаблон, но добавить еще один шаблон для < играть >, которые не соответствуют условию, чтобы превратить эти < игру > в ничто:

<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> 
+0

Спасибо! Теперь это работает как шарм – Msencenb

+0

@Msencenb: Это не дает желаемого результата. – 2010-10-20 21:39:53

+0

Вы уверены? Самый первый приведенный пример кода работает для меня – Msencenb

1

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

<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> 
+0

Это хорошо работает и использует ключи, как другой ответ. Я считаю, что ключевой вывод для меня заключается в том, что условия должны быть в выборе, а не в определении соответствия. – Msencenb

+0

@Msencenb: Согласование шаблонов, управляемое против выбора, зависит от стиля pull и push. Оба в порядке. В этом случае, поскольку я использую параметр в таблице стилей XSLT 1.0, мне пришлось идти с типом push (ссылки в параграфе/var не допускаются в шаблонах XSLT 1.0) – 2010-10-21 12:10:43

+0

@Alejandro, @@ Msencenb: ссылки на переменные/параметры разрешено в шаблоне соответствия в XSLT 2.0 –

5

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> 
+0

Я даже не слышал о ключах ... но это, по-видимому, наиболее эффективно. Спасибо – Msencenb

+0

@Msencenb: Обратите внимание, что решение XSLT 2.0 вообще не использует ключи. Их роль играет ''. Но, конечно, даже в XSLT 2.0 это не повредит, и действительно может быть полезно, чтобы знать, как использовать ключи. –

+0

@Dimitre: я не пошел с двумя ключами, потому что это выражение '* */play [generate-id() ...]' будет перебирать все элементы 'play' в любом случае. – 2010-10-21 12:05:46