W XSLT jak zwiększyć zmienną globalną z innego zakresu?

Przetwarzam plik XML, w którym chcę zachować liczbę węzłów, aby móc używać go jako ID podczas pisania nowych węzłów.

W tej chwili mam zmienną globalną o nazwie 'counter'. Jestem w stanie uzyskać do niego dostęp w szablonie, ale nie znalazłem sposobu na manipulowanie nim w szablonie.

Oto skondensowana wersja mojego pliku XSLT:

<xsl:variable name="counter" select="1" as="xs:integer"/>

<xsl:template match="/"> 
   <xsl:for-each select="section">
      <xsl:call-template name="section"></xsl:call-template>
   </xsl:for-each>
</xsl:template>

<xsl:template name="section">

   <!-- Increment 'counter' here -->

   <span class="title" id="title-{$counter}"><xsl:value-of select="title"/></span>
</xsl:template>

Jakieś sugestie, jak stąd wyjść?

Author: Marcel, 2009-05-07

8 answers

Inni już wyjaśnili, jak zmienne są niezmienne-że w XSLT nie ma instrukcji przypisania (jak w czysto funkcyjnych językach programowania w ogóle).

Mam alternatywę dla rozwiązań, które zostały zaproponowane do tej pory. Unika przekazywania parametrów(co jest gadatliwe i brzydkie w XSLT-nawet to przyznam).

W XPath można po prostu policzyć liczbę <section> elementów poprzedzających bieżący:

<xsl:template name="section">
  <span class="title" id="title-{1 + count(preceding-sibling::section)}">
    <xsl:value-of select="title"/>
  </span>
</xsl:template>

(uwaga: kod spacji formatowanie nie pojawi się w wyniku, ponieważ węzły tekstowe z białymi spacjami są automatycznie usuwane z arkusza stylów. Więc nie czuj się zmuszony do umieszczania instrukcji na tej samej linii.)

Dużą zaletą tego podejścia (w przeciwieństwie do używania position()) jest to, że jest ono zależne tylko od bieżącego węzła, a nie od bieżącej listy węzłów. Jeśli w jakiś sposób zmieniłeś swoje przetwarzanie (np. <xsl:for-each> przetwarza nie tylko sekcje, ale także jakiś inny element), wtedy wartość position() nie będzie już musi odpowiadać pozycji <section> elementów w dokumencie. Z drugiej strony, jeśli użyjesz count() Jak wyżej, to zawsze będzie ona odpowiadała pozycji każdego <section> elementu. Takie podejście zmniejsza sprzężenie z innymi częściami kodu, co jest generalnie bardzo dobrą rzeczą.

Alternatywą dla count () byłoby użycie instrukcji <xsl:number>. Jego domyślne zachowanie będzie numerować wszystkie elementy o podobnej nazwie na tym samym poziomie, co się dzieje chcesz:

<xsl:template name="section">
  <xsl:variable name="count">
    <xsl:number/>
  </xsl:variable>
  <span class="title" id="title-{$count}">
    <xsl:value-of select="title"/>
  </span>
</xsl:template>

Jest to kompromis w słowności (wymagający dodatkowej deklaracji zmiennej, jeśli nadal chcesz używać szablonu wartości atrybutu klamry), ale tylko nieznacznie, ponieważ również drastycznie upraszcza wyrażenie XPath.

Jest jeszcze więcej miejsca na poprawę. Chociaż usunęliśmy zależność od bieżącej listy węzłów, nadal jesteśmy zależni od bieżącego węzła. To samo w sobie nie jest złe, ale nie jest od razu jasne, patrząc na szablon co to jest bieżący węzeł. Wiemy tylko, że szablon ma nazwę " section"; aby mieć pewność, co jest przetwarzane, musimy poszukać gdzie indziej w naszym kodzie. Ale nawet to nie musi tak być.

Jeśli kiedykolwiek poczujesz potrzebę używania <xsl:for-each> i <xsl:call-template> razem( jak w twoim przykładzie), cofnij się i dowiedz się, jak używać <xsl:apply-templates>.

<xsl:template match="/doc">
  <xsl:apply-templates select="section"/>
</xsl:template>

<xsl:template match="section">
  <xsl:variable name="count">
    <xsl:number/>
  </xsl:variable>
  <span class="title" id="title-{$count}">
    <xsl:value-of select="title"/>
  </span>
</xsl:template>

To podejście nie tylko jest mniej wyraziste (<xsl:apply-templates/> zastępuje zarówno <xsl:for-each>, jak i <xsl:call-template/>), ale również staje się natychmiast jasne, co aktualnym węzłem jest. Wystarczy spojrzeć na atrybut match i od razu wiesz, że przetwarzasz element <section> i że elementy <section> są tym, co liczysz.

Aby uzyskać zwięzłe wyjaśnienie, jak działają reguły szablonu (tj. <xsl:template> elementy, które mają atrybut match), zobacz"Jak działa XSLT".

 44
Author: Evan Lenz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-13 09:31:50

Zmienne XSLT nie mogą być zmieniane. Będziesz musiał przekazać wartość z szablonu do szablonu.

Jeśli używasz XSLT 2.0, możesz mieć parametry i używać tunelowania, aby propagować zmienną do właściwych szablonów.

Twój szablon będzie wyglądał mniej więcej tak:

<xsl:template match="a">
<xsl:param name="count" select="0">
  <xsl:apply-templates>
     <xsl:with-param select="$count+1"/>
  </xsl:apply-templates>
</xsl:template>

Spójrz również na użycie generate-id (), jeśli chcesz utworzyć identyfikatory.

 8
Author: BeWarned,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-07 06:25:25

Zmienne w XSLT są niezmienne, więc musisz o tym pamiętać. Możesz użyć position() bezpośrednio:

<xsl:template match="/"> 
   <xsl:for-each select="section">
      <xsl:call-template name="section"/>
   </xsl:for-each>
</xsl:template>

<xsl:template name="section">
   <span class="title" id="title-{position()}"><xsl:value-of select="title"/></span>
</xsl:template>

Lub w sposób bardziej zorientowany na szablon:

<xsl:template match="/"> 
   <xsl:apply-templates select="section"/>
</xsl:template>

<xsl:template match="section">
   <span class="title" id="title-{position()}"><xsl:value-of select="title"/></span>
</xsl:template>
 6
Author: jelovirt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-07 11:56:03

Zmienne są lokalnie skalowane i tylko do odczytu w xslt.

 2
Author: Luixv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-07 06:20:30

W zależności od Twojego procesora XSLT, możesz być w stanie wprowadzić funkcje skryptowe do twojego XLST. Na przykład Biblioteka Microsoft XML obsługuje włączenie javascript. Zobacz http://msdn.microsoft.com/en-us/library/aa970889 (VS. 85). aspx dla przykładu. Ta taktyka oczywiście nie zadziała, jeśli planujesz wdrożyć / wykonać XSLT na przeglądarkach klientów publicznych; musi to być zrobione przez określony procesor XSLT.

 2
Author: Alan McBee - MSFT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-07 06:30:09

Możesz użyć funkcji position (), aby zrobić to, co chcesz. Wyglądałoby to mniej więcej tak.

<xsl:template match="/">
  <xsl:for-each select="section">
    <xsl:call-template name="section">
      <xsl:with-param name="counter" select="{position()}"/>
    </xsl:call-template>
  </xsl:for-each>
</xsl:template>

<xsl:template name="section">
  <xsl:param name="counter"/>
  <span class="title" id="title-{$counter}">
    <xsl:value-of select="title"/>
  </span>
</xsl:template>
 1
Author: Bhushan Bhangale,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-07 06:37:29

Sam tego nie próbowałem, ale możesz spróbować przekazać parametr do szablonu. W pierwszym szablonie Ustawiłeś parametr na count () (lub current ()?) wewnątrz instrukcji for-each, a następnie przekaż tę wartość do szablonu "section".

Oto więcej o przekazywaniu parametrów do szablonów

 0
Author: autonomatt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-07 06:29:40

Użyj <xsl:variable name="RowNum" select="count(./preceding-sibling::*)" /> oraz $RowNum jako wartość zwiększającą.

Eg: <xsl:template name="ME-homeTiles" match="Row[@Style='ME-homeTiles']" mode="itemstyle"> <xsl:variable name="RowNum" select="count(./preceding-sibling::*)" /> ...<a href="{$SafeLinkUrl}" class="tile{$RowNum}"><img ....></a>

Spowoduje utworzenie klas dla linku o wartościach tile1, tile2, tile3 itd...

 0
Author: marika.daboja,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-21 23:57:05