W jakiej kolejności wykonują się szablony w dokumencie XSLT i czy pasują do źródłowego XML lub buforowanego wyjścia?

Oto coś, co zawsze mnie zadziwiało w XSLT:

  1. w jakiej kolejności wykonują się szablony i
  2. kiedy wykonują, czy pasują do (a) oryginalnego źródłowego XML, czy (b) bieżącego wyjścia XSLT do tego punktu?

Przykład:

<person>
  <firstName>Deane</firstName>
  <lastName>Barker</lastName>
</person>

Oto fragment XSLT:

<!-- Template #1 -->
<xsl:template match="/">
  <xsl:value-of select="firstName"/> <xsl:value-of select="lastName"/>
</xsl:template>

<!-- Template #2 -->
<xsl:template match="/person/firstName">
  First Name: <xsl:value-of select="firstName"/>
</xsl:template>

Dwa pytania na ten temat:

  1. zakładam, że szablon # 1 uruchomi się jako pierwszy. Nie wiem, dlaczego zakładam, że ... pojawia się jako pierwszy w dokumencie?
  2. czy szablon # 2 zostanie wykonany? Pasuje do węzła w źródłowym XML, ale zanim dotrzemy do tego szablonu (zakładając, że działa jako drugi), węzeł "firstName" nie będzie w drzewie wyjściowym.

Czy zatem" późniejsze "szablony są zależne od tego, co miało miejsce w" wcześniejszych "szablonach, czy też działają na dokumencie źródłowym, nie zważając na to, co zostało przekształcone "przed" do nich? (Wszystkie te słowa są w cudzysłowach, bo trudno mi o nich dyskutować kwestie czasowe, kiedy naprawdę nie mam pojęcia, jak kolejność szablonów jest ustalana w pierwszej kolejności...)

W powyższym przykładzie mamy szablon, który pasuje do węzła głównego ( " / " ), który-po zakończeniu wykonywania-zasadniczo usunął wszystkie węzły z wyjścia. Skoro tak, to czy byłoby to wykluczone z wykonania wszystkich innych szablonów, skoro po skompletowaniu pierwszego szablonu nie ma co dopasowywać?

Do tej pory zajmowałem się późniejszymi szablonami nie wykonują, ponieważ węzły, na których działają, nie pojawiają się na wyjściu, ale co z odwrotnością? Czy szablon "wcześniejszy" może stworzyć węzeł, z którym szablon "późniejszy" może coś zrobić?

W tym samym XML jak powyżej, rozważ ten XSL:

<!-- Template #1 -->
<xsl:template match="/">
  <fullName>
    <xsl:value-of select="firstName"/> <xsl:value-of select="lastName"/>
  </fullName>
</xsl:template>

<!-- Template #2 -->
<xsl:template match="//fullName">
  Full Name: <xsl:value-of select="."/>
</xsl:template>

Szablon # 1 tworzy nowy węzeł o nazwie "fullName". Szablon # 2 pasuje do tego samego węzła. Czy szablon # 2 uruchomi się, ponieważ węzeł "fullName" istnieje w wyjściu do czasu, gdy przejdziemy do szablonu #2?

I uświadom sobie, że jestem głęboko nieświadomy "zen" XSLT. Do tej pory moje arkusze stylów składały się z szablonu pasującego do węzła głównego, a następnie są całkowicie proceduralne. Mam tego dość. Wolałbym właściwie zrozumieć XSLT, stąd moje pytanie.

 65
Author: Deane, 2009-10-07

4 answers

Podoba mi się Twoje pytanie. Jesteś bardzo elokwentny o tym, czego jeszcze nie rozumiesz. Potrzebujesz tylko czegoś, co połączy wszystko. Zalecam przeczytanie "Jak działa XSLT", rozdziału, który napisałem, aby dokładnie odpowiedzieć na pytania, które zadajesz. Chciałbym usłyszeć, czy to coś łączy.

Mniej formalnie, odpowiem na każde twoje pytanie.
  1. w jakiej kolejności wykonują się szablony i
  2. kiedy wykonują, czy pasują do (a) oryginalnego źródłowego XML, czy (b) bieżące wyjście XSLT do tego punkt?

W dowolnym momencie przetwarzania XSLT istnieją, w pewnym sensie, dwa konteksty, które identyfikujesz jako (a) i (b): gdzie jesteś w drzewie źródłowym i gdzie jesteś w drzewie wynikowym }. Gdzie jesteś w drzewie źródłowym nazywa się bieżący węzeł. Może zmieniać się i skakać po całym drzewie źródeł, jak wybierzesz dowolne zestawy węzłów do przetworzenia przy użyciu XPath. Jednak koncepcyjnie nigdy nie "przeskakujesz" drzewa wyników w ten sam sposób. Procesor XSLT konstruuje go w sposób uporządkowany; najpierw tworzy węzeł korzeniowy drzewa wyników; następnie dodaje dzieci, budując wynik w kolejności dokumentu (depth-first). [Twój post motywuje mnie do ponownego odebrania wizualizacji oprogramowania do eksperymentów XSLT...]

Kolejność reguł szablonu w arkuszu stylów nie ma znaczenia. Nie można powiedzieć, tylko przez patrząc na arkusz stylów, w jakiej kolejności reguły szablonów będą tworzone, ile razy reguła będzie tworzona, a nawet czy w ogóle będzie. (match="/" jest wyjątkiem; zawsze możesz wiedzieć, że zostanie uruchomiony.)

Zakładam, że szablon # 1 będzie wykonać pierwszy. I don ' t know why I Załóżmy, że ... pojawia się jako pierwszy w dokumencie?

Nie. Zostanie wywołany jako pierwszy, nawet jeśli umieścisz go jako ostatni w dokumencie. Kolejność reguł szablonu nigdy nie ma znaczenia (poza warunkiem błędu, gdy masz więcej niż jedną regułę szablonu o tym samym priorytecie pasującą do tego samego węzła; nawet wtedy jest to opcjonalne dla implementatora i nigdy nie powinieneś polegać na takim zachowaniu). Jest wywoływany jako pierwszy, ponieważ pierwszą rzeczą, która zawsze dzieje się za każdym razem, gdy uruchamiasz procesor XSLT, jest wirtualne wywołanie <xsl:apply-templates select="/"/> . Jedno wirtualne wywołanie konstruuje całe drzewo wyników. Nic nie dzieje się poza nim. Możesz dostosować lub "configure", zachowanie tej Instrukcji poprzez zdefiniowanie reguł szablonu.

Czy szablon # 2 zostanie wykonany? Pasuje do węzła w źródłowym XML, ale zanim dojdziemy do tego szablon (zakładając, że działa jako drugi), węzeł" firstName " nie będzie w drzewo wyjściowe.

Szablon # 2 (ani żadne inne zasady szablonu) nigdy nie zostanie uruchomiony, chyba że masz <xsl:apply-templates/> wywołanie gdzieś w match="/" reguły. Jeśli nie masz żadnych, To nie ma innych reguł szablonu niż match="/" zostanie uruchomiony. Pomyśl o tym w ten sposób: aby reguła szablonu została uruchomiona, nie może ona po prostu dopasować węzła do wejścia. Musi pasować do węzła, który wybierzesz do procesu (używając <xsl:apply-templates/>). I odwrotnie, będzie on nadal pasował do węzła tyle razy, ile zdecydujesz się go przetworzyć.

Would [the match="/" szablon] wypuść wszystkie inne szablony od wykonania, ponieważ nie ma nic do dopasowania po tym pierwszym szablonie jest kompletna?

Ta zasada uprzedza resztę przez nigdzie w tym <xsl:apply-templates/> w nim. Istnieje jeszcze wiele węzłów, które mogłyby być przetwarzane w drzewie źródłowym. Zawsze są tam wszystkie, Dojrzałe do zbioru; przetwarzaj każdy z nich tyle razy, ile chcesz. Ale jedynym sposobem na ich przetworzenie przy użyciu reguł szablonu jest wywołanie <xsl:apply-templates/>.

Do tego momentu, byłem zaniepokojony z późniejszymi szablonami nie wykonywanymi ponieważ węzły, które obsługiwały on nie pojawia się na wyjściu, ale a co z odwrotnie? Can an "wcześniejszy" szablon tworzy węzeł, który szablon" później " może coś zrobić z?

Nie chodzi o to, że" wcześniejszy " szablon tworzy nowy węzeł do przetworzenia; chodzi o to, że "wcześniejszy" szablon z kolei przetwarza więcej węzłów z drzewa źródłowego, używając tej samej instrukcji (<xsl:apply-templates). Można o tym myśleć jako o wywołaniu tej samej "funkcji" rekurencyjnie, z różnymi parametrami za każdym razem (węzły do przetworzenia określone przez kontekst i select atrybut).

W końcu otrzymujesz stos rekurencyjnych wywołań tej samej "funkcji" (<xsl:apply-templates>). I ta struktura drzewa jest izomorficzna do twojego rzeczywistego wyniku. Nie każdy zdaje sobie z tego sprawę lub myślał o tym w ten sposób; dzieje się tak dlatego, że nie mamy żadnych skutecznych narzędzi wizualizacji...jeszcze.

Szablon # 1 tworzy nowy węzeł o nazwie "fullName". Szablon #2 pasuje do ten sam węzeł. Szablon Will #2 wykonaj ponieważ " fullName" węzeł istnieje w wyjściu do czasu, gdy przejdź do szablonu #2?

Nie. Jedynym sposobem, aby zrobić łańcuch przetwarzania jest wyraźnie skonfigurować go w ten sposób. Utwórz zmienną, np. $tempTree, która zawiera nowy element <fullName>, a następnie przetworz go , tak jak to <xsl:apply-templates select="$tempTree">. Aby to zrobić w XSLT 1.0, musisz zawinąć odniesienie do zmiennej z funkcją rozszerzenia (np. exsl:node-set()), ale w XSLT 2.0 będzie działać tak, jak jest.

Czy przetwarzasz węzły z oryginalne drzewo źródłowe lub tymczasowe drzewo, które konstruujesz, tak czy inaczej musisz wyraźnie powiedzieć, które węzły chcesz przetworzyć.

Nie omówiliśmy tego, jak XSLT uzyskuje wszystkie swoje ukryte zachowania. Musisz również zrozumieć wbudowane reguły szablonów . Cały czas piszę arkusze stylów, które nie zawierają nawet jawnej reguły dla węzła głównego (match="/"). Zamiast tego polegam na wbudowanej zasadzie dla węzłów głównych( stosuj szablony do dzieci), która jest taka sama jak wbudowana Reguła dla węzłów elementów. W ten sposób mogę zignorować duże części wejścia, pozwolić procesorowi XSLT automatycznie go przemierzać i tylko wtedy, gdy natknie się na interesujący mnie węzeł, zrobię coś specjalnego. Albo mógłbym napisać jedną regułę, która kopiuje wszystko rekurencyjnie (zwaną transformacją tożsamości), nadpisując ją tylko wtedy, gdy jest to konieczne, aby dokonać przyrostowych zmian na wejściu. Po przeczytaniu "jak działa XSLT", następnym zadaniem jest sprawdzenie "transformacji tożsamości".

Zdaję sobie sprawę, że jestem głęboko nieświadomy o" zen " XSLT. Do tej pory, mój arkusze stylów składały się z szablon pasujący do węzła głównego, następnie są całkowicie proceduralne. Mam tego dość. Ja bym raczej zrozumieć XSLT słusznie, stąd moje pytanie.

Brawo. Teraz nadszedł czas, aby wziąć "czerwoną pigułkę": przeczytaj "Jak działa XSLT"
 82
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-10-09 09:13:57

Szablony Zawsze pasują do źródłowego XML. Tak więc kolejność nie ma znaczenia, chyba że 2 lub więcej szablonów pasuje do tych samych węzłów. W takim przypadku, nieco wbrew intuicji, reguła z ostatnim dopasowującym szablonem jest uruchamiana.

 6
Author: mirod,
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-10-07 14:06:03

W 1. przykładowym szablonie # 1 uruchamia się, ponieważ po rozpoczęciu przetwarzania wejściowego xml zaczyna się on od elementu głównego i jest to jedyny szablon w arkuszu stylów, który pasuje do elementu głównego. Nawet gdyby był 2. w arkuszu stylów, nadal działałby 1.

W tym przykładzie szablon 2 nie będzie uruchamiany, ponieważ już przetworzyłeś element główny za pomocą szablonu 1 i nie ma więcej elementów do przetworzenia za elementem głównym. Jeśli chcesz przetwarzać inne elementy za pomocą dodatkowych szablonów powinieneś to zmienić.

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

To pozwala zdefiniować szablon dla każdego elementu, który Cię interesuje i przetwarzać xml w bardziej logiczny sposób, zamiast robić to proceduralnie.

Zauważ również, że ten przykład nie wyświetli niczego, ponieważ w bieżącym kontekście (głównym) nie ma elementu firstName, tylko element person, więc powinien być:

<xsl:template match="/">
  <xsl:value-of select="person/firstName"/> <xsl:value-of select="person/lastName"/>
</xsl:template>

Łatwiej jest mi myśleć, że przechodzisz przez xml, zaczynając od katalogu głównego i szukając szablon, który pasuje do tego elementu, a następnie postępuj zgodnie z tymi instrukcjami, aby wygenerować wyjście Teh. XSLT przekształca Dokument wejściowy na wyjście, dzięki czemu dokument wyjściowy jest pusty na początku transformacji. Wyjście nie jest używane jako część transformacji jest tylko wyjściem z niego.

W drugim przykładzie szablon # 2 nie zostanie wykonany, ponieważ szablon jest uruchamiany z wejściowym xml, a nie z wyjściowego.

 2
Author: Chris R,
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-10-07 14:53:56

Odpowiedź Evana jest w zasadzie dobra.

Jednak jedna rzecz, której wydaje się brakować, to zdolność do "wywoływania" fragmentów kodu bez dopasowywania. Umożliwiłoby to - przynajmniej w opinii niektórych-znacznie lepszą strukturyzację.

Zrobiłem mały przykład, aby pokazać, co mam na myśli.
<xsl:template match="/" name="dotable">
<!-- Surely the common html part could be placed somewhere else -->
    <!-- the head and the opening body -->
<html>
<head><title>Salary table details</title></head>

    <body>
<!-- Comments are better than nothing -->
    <!-- but that part should really have been somewhere else ... -->

<!-- Now do what we really want here ... this really is making the table! -->

<h1>Salary Table</h1>
<table border = "3" width="80%">
<xsl:for-each select="//entry">
    <tr>
        <td><xsl:value-of select="name" /></td>
        <td><xsl:value-of select="firstname" /></td>
        <td><xsl:value-of select="age" /></td>
        <td><xsl:value-of select="salary" /></td>
    </tr>
</xsl:for-each>
</table>

<!-- Now close out the html -->
</body>
</html>
<!-- this should also really be somewhere else -->

<!-- This approach works, but leads to horribly monolithic code -->
    <!-- Further - it leads to templates including code which is strictly -->
    <!-- not relevant to them. I've not found a way round this yet -->
</xsl:template>

Jednak, po trochę pokręcić, i na początku korzystając z podpowiedzi, że jeśli są dwa pasujące szablony, ostatni w kodzie będzie wybrany, a następnie restrukturyzacji mojego kodu (nie wszystkie pokazane tutaj), osiągnąłem to, co wydaje się działać, i miejmy nadzieję, generuje poprawny kod, a także wyświetlanie poszukiwanych danych -

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- <?xml version="1.0"?>-->

<xsl:template name="dohtml">
  <html>
      <xsl:call-template name="dohead" />
      <xsl:call-template name="dobody" />
  </html>
</xsl:template>

<xsl:template name="dohead">
<head>
    <title>Salary details</title>
</head>
</xsl:template>

<xsl:template name="dobody">
<body>
    <xsl:call-template name="dotable" />
</body>
</xsl:template>

<xsl:template match="/entries" name="dotable">

<h1>Salary Table</h1>
<table border = "3" width="80%">
<xsl:for-each select="//entry">
    <tr>
        <td><xsl:value-of select="name" /></td>
        <td><xsl:value-of select="firstname" /></td>
        <td><xsl:value-of select="age" /></td>
        <td><xsl:value-of select="salary" /></td>
    </tr>
</xsl:for-each>
</table>

</xsl:template>

<xsl:template  match="/" name="main">
            <xsl:call-template name="dohtml" />
</xsl:template> 

[Przewiń kod powyżej góra-dół, jeśli nie widzisz wszystkiego]

To działa tak, że główny szablon zawsze pasuje - pasuje na /

To ma kawałki kodu-szablony-które są nazywane.

To teraz oznacza, że nie jest możliwe dopasowanie inny szablon na / ale można dopasować jawnie na nazwanym węźle, który w tym przypadku jest węzłem najwyższego poziomu we wpisach wywołanych xml.

Mała modyfikacja kodu dała przykład podany powyżej.

 0
Author: user2151421,
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-03-09 13:12:18