In welke volgorde worden sjablonen in een XSLT-document uitgevoerd en komen ze overeen op de bron-XML of de gebufferde uitvoer?

Hier is iets dat me altijd heeft verbaasd over XSLT:

  1. In welke volgorde worden de sjablonen uitgevoerd, en
  2. Als ze worden uitgevoerd, komen ze overeen met (a) de originele bron-XML, of (b) de huidige uitvoer van de XSLT tot dat punt?

Voorbeeld:

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

Hier is een fragment van 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>

Twee vragen hierover:

  1. Ik ga ervan uit dat sjabloon #1 als eerste wordt uitgevoerd. Ik weet niet waarom ik dit aanneem — is het alleen omdat het als eerste in het document voorkomt?
  2. Wordt sjabloon #2 uitgevoerd? Het komt overeen met een knooppunt in de bron-XML, maar tegen de tijd dat we bij deze sjabloon komen (ervan uitgaande dat het als tweede wordt uitgevoerd), bevindt het “firstName”-knooppunt zich niet in de uitvoerstructuur.

Dus, zijn “latere” sjablonen afhankelijk van wat er is gebeurd in “eerdere” sjablonen, of werken ze op het brondocument, zich niet bewust van wat er “vóór” aan hen is getransformeerd? (Al die woorden staan ​​tussen aanhalingstekens, omdat ik het moeilijk vind om op tijd gebaseerde problemen te bespreken als ik echt geen idee heb hoe de sjabloonvolgorde in de eerste plaats wordt bepaald…)

In het bovenstaande voorbeeld hebben we een sjabloon die overeenkomt met het hoofdknooppunt (“/”) dat – als het klaar is met uitvoeren – in wezen alle knooppunten uit de uitvoer heeft verwijderd. Als dit het geval is, zou dit voorkomen dat alle andere sjablonen worden uitgevoerd omdat er niets is om op te matchen nadat die eerste sjabloon is voltooid?

Tot nu toe maakte ik me zorgen over latere sjablonen die niet werden uitgevoerd omdat de knooppunten waarop ze hebben gewerkt niet in de uitvoer verschijnen, maar hoe zit het met het omgekeerde? Kan een “eerder” sjabloon een knooppunt maken waar een “later” sjabloon iets mee kan doen?

Overweeg op dezelfde XML als hierboven deze 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>

Template #1 maakt een nieuw knooppunt met de naam “fullName”. Sjabloon #2 komt overeen op datzelfde knooppunt. Zal sjabloon #2 worden uitgevoerd omdat het “fullName”-knooppunt in de uitvoer bestaat tegen de tijd dat we sjabloon #2 bereiken?

Ik realiseer me dat ik diep onwetend ben over de “zen” van XSLT. Tot op heden bestonden mijn stylesheets uit een sjabloon die overeenkomt met het hoofdknooppunt, en zijn vanaf daar volledig procedureel. Ik ben het zat om dit te doen. Ik zou XSLT eigenlijk liever goed begrijpen, vandaar mijn vraag.


Antwoord 1, autoriteit 100%

Ik vind je vraag geweldig. Je bent heel welbespraakt over wat je nog niet begrijpt. Je hebt gewoon iets nodig om dingen aan elkaar te binden. Mijn aanbeveling is dat je “How XSLT Works”leest, een hoofdstuk dat ik schreef om precies de vragen die je stelt. Ik hoor graag of het voor jou iets met elkaar verbindt.

Minder formeel, ik zal proberen al uw vragen te beantwoorden.

  1. In welke volgorde worden de sjablonen uitgevoerd, en
  2. Als ze worden uitgevoerd, komen ze overeen met (a) de originele bron-XML, of (b)
    de huidige output van de XSLT naar dat
    punt?

Op elk willekeurig punt in XSLT-verwerking zijn er in zekere zin twee contexten, die u identificeert als (a) en (b): waar u zich bevindt in de bronstructuur, en waar u bevindt zich in de resultatenboom. Waar u zich in de bronstructuur bevindt, wordt het huidige knooppuntgenoemd. Het kan veranderen en overal in de bronboom springen, terwijl je willekeurige sets knooppunten kiest om te verwerken met behulp van XPath. Conceptueel “springt” u echter nooit op dezelfde manier rond de resultatenboom. De XSLT-processor bouwt het op een geordende manier op; eerst maakt het het hoofdknooppunt van de resultatenboom; dan voegt het kinderen toe en bouwt het resultaat op in documentvolgorde (diepte eerst). [Je bericht motiveert me om mijn softwarevisualisatie voor XSLT-experimenten weer op te pakken…]

De volgorde van sjabloonregels in een stylesheet doet er niet toe. Je kunt niet zeggen, alleen door naar de stylesheet te kijken, in welke volgorde de sjabloonregels zullen worden geïnstantieerd, hoe vaak een regel zal worden geïnstantieerd, of zelfs of het überhaupt zal worden uitgevoerd. (match="/"is een uitzondering; je kunt er altijd zeker van zijn dat deze wordt geactiveerd.)

Ik ga ervan uit dat sjabloon #1 dat wel zal doen
eerst uitvoeren. ik weet niet waarom ik
neem dit aan — is het gewoon omdat het
verschijnt eerst in het document?

Nee. Het zou als eerste worden aangeroepen, zelfs als u het als laatste in het document plaatst. Volgorde van sjabloonregels is nooit van belang (behalve bij een fout wanneer u meer dan één sjabloonregel met dezelfde prioriteit hebt die overeenkomt met hetzelfde knooppunt; zelfs dan is het optioneel voor de implementator en moet u nooit op dergelijk gedrag vertrouwen). Het wordt als eerste aangeroepen omdat het eerste dat altijdgebeurt wanneer u een XSLT-processor uitvoert, een virtuele aanroep is naar <xsl:apply-templates select="/"/> . De ene virtuele oproep construeert de volledige resultatenboom. Daarbuiten gebeurt niets. U kunt het gedrag van die instructie aanpassen of “configureren” door sjabloonregels te definiëren.

Wordt sjabloon #2 uitgevoerd? Het komt overeen met een knooppunt in de bron-XML, maar
tegen de tijd dat we dit bereiken
sjabloon (ervan uitgaande dat het als tweede wordt uitgevoerd),
de “firstName” node zal niet in
de uitvoerboom.

Sjabloon #2 (noch andere sjabloonregels) worden nooit geactiveerd, tenzij je een <xsl:apply-templates/>-aanroep ergens in de match="/"regel. Als je er geen hebt, worden er geen andere sjabloonregels dan match="/"geactiveerd. Zie het op deze manier: om een ​​sjabloonregel te activeren, kan deze niet zomaar overeenkomen met een knooppunt in de invoer. Het moet overeenkomen met een knooppunt dat u kiest om te verwerken(met behulp van <xsl:apply-templates/>). Omgekeerd blijft het zo vaak overeenkomen met het knooppunt als u ervoor kiest om het te verwerken.

Zou [de match="/"
sjabloon] preëmpt alle andere sjablonen
van het uitvoeren omdat er niets is
om op te passen na die eerste sjabloon
is voltooid?

Die regel heeft geen voorrang op de rest, inclusief <xsl:apply-templates/>erin. Er zijn nog tal van nodes die kunnenverwerkt worden in de source tree. Ze zijn er altijd allemaal, rijp om geplukt te worden; verwerk ze allemaal zo vaak als je wilt. Maar de enige manier om ze te verwerken met behulp van sjabloonregels, is door <xsl:apply-templates/>aan te roepen.

Tot nu toe maakte ik me zorgen
met latere sjablonen die niet worden uitgevoerd
omdat de knooppunten die ze hebben gebruikt
aan verschijnen niet in de uitvoer, maar
hoe zit het met het omgekeerde? kan een
“eerdere” sjabloon maakt een knooppunt dat
een “later” sjabloon kan iets doen
met?

Het is niet zo dat een “eerder” sjabloon een nieuw knooppunt maakt dat moet worden verwerkt; het is dat een “eerder” sjabloon op zijn beurt meer knooppunten uit de bronstructuur verwerkt, met dezelfde instructie (<xsl:apply-templates). Je kunt het zien als het recursief aanroepen van dezelfde “functie”, met elke keer andere parameters (de te verwerken knooppunten zoals bepaald door de context en het selectattribuut).

Uiteindelijk krijg je een boomgestructureerde stapel recursieve aanroepen naar dezelfde “functie” (<xsl:apply-templates>). En deze boomstructuur is isomorfvoor uw werkelijke resultaat. Niet iedereen beseft dit of heeft er zo over nagedacht; dat komt omdat we nog geen effectieve visualisatietools hebben.

Template #1 maakt een nieuw knooppunt met de naam
“voor-en achternaam”. Sjabloon #2 komt overeen met
datzelfde knooppunt. Zal sjabloon #2
uitvoeren omdat de “fullName” node
bestaat in de uitvoer tegen de tijd dat we
om naar sjabloon #2 te gaan?

Nee. De enige manier om een ​​verwerkingsketen uit te voeren, is door deze expliciet op die manier in te stellen. Maak een variabele, bijv. $tempTree, die het nieuwe element <fullName>bevat en verwerk vervolgens it, zoals deze <xsl:apply-templates select="$tempTree">. Om dit in XSLT 1.0 te doen, moet u de variabeleverwijzing omwikkelen met een extensiefunctie (bijv. exsl:node-set()), maar in XSLT 2.0 werkt het zoals het is.

Of je nu knooppunten uit de oorspronkelijke bronstructuur verwerkt of in een tijdelijke boomstructuur die je maakt, je moet hoe dan ook expliciet aangeven welke knooppunten je wilt verwerken.

Wat we niet hebben besproken, is hoe XSLT al zijn impliciete gedrag krijgt. U moet ook de ingebouwde sjabloonregelsbegrijpen. Ik schrijf de hele tijd stylesheets die niet eens een expliciete regel bevatten voor het hoofdknooppunt (match="/"). In plaats daarvan vertrouw ik op de ingebouwde regel voor hoofdknooppunten (pas sjablonen toe op kinderen), die hetzelfde is als de ingebouwde regel voor elementknooppunten. Zo kan ik grote delen van de invoer negeren, de XSLT-processor er automatisch doorheen laten lopen, en pas als hij een node tegenkomt waarin ik geïnteresseerd ben, ga ik iets speciaals doen. Of ik zou een enkele regel kunnen schrijven die alles recursief kopieert (de identiteitstransformatie genoemd), en deze alleen waar nodig overschrijft om incrementele wijzigingen aan de invoer aan te brengen. Nadat je “Hoe XSLT werkt” hebt gelezen, is je volgende opdracht om de “identiteitstransformatie” op te zoeken.

Ik besef dat ik diep onwetend ben
over de “zen” van XSLT. Tot op heden, mijn
stylesheets bestonden uit een
sjabloon die overeenkomt met het hoofdknooppunt, dan
zijn vanaf daar volledig procedureel.
Ik ben het zat om dit te doen. ik zou
begrijp XSLT eigenlijk liever
correct, vandaar mijn vraag.

Ik juich je toe. Nu is het tijd om de “rode pil” te nemen: lees “Hoe XSLT werkt”


Antwoord 2, autoriteit 6%

Sjablonen altijdkomen overeen in de bron-XML. De volgorde maakt dus niet zoveel uit, tenzij 2 of meer sjablonen overeenkomen met dezelfde node(s). In dat geval wordt, enigszins contra-intuïtief, de regel met de laatsteovereenkomende sjabloon geactiveerd.


Antwoord 3, autoriteit 2%

In uw eerste voorbeeld wordt sjabloon #1 uitgevoerd omdat wanneer u de invoer xml begint te verwerken, deze begint bij de root en dat is de enige sjabloon in uw stylesheet die overeenkomt met het root-element. Zelfs als het de 2e was in de stylesheet, zou het nog steeds de 1e zijn.

In dit voorbeeld zal sjabloon 2 niet worden uitgevoerd omdat je het root-element al hebt verwerkt met sjabloon 1 en er geen elementen meer zijn om te verwerken na de root. Als u andere elementen wilt verwerken met behulp van aanvullende sjablonen, moet u dit wijzigen in.

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

Hierdoor kunt u een sjabloon definiëren voor elk element waarin u geïnteresseerd bent en de XML op een meer logische manier verwerken, in plaats van dit procedureel te doen.

Houd er rekening mee dat dit voorbeeld niets zal uitvoeren, aangezien er in de huidige context (de root) geen firstName-element is, alleen een person-element, dus het zou moeten zijn:

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

Ik vind het makkelijker om te denken dat je door de xml stapt, beginnend bij de root en op zoek bent naar de sjabloon die overeenkomt met dat element en dan die instructies volgt om de uitvoer te genereren. De XSLT transformeert het invoerdocument naar de uitvoer zodat het uitvoerdocument aan het begin van de transformatie leeg is. De uitvoer wordt niet gebruikt als onderdeel van de transformatie, het is alleen de uitvoer ervan.

In uw tweede voorbeeld zal sjabloon #2 niet worden uitgevoerd omdat de sjabloon wordt uitgevoerd tegen de invoer xml en niet tegen de uitvoer.


Antwoord 4

Evans antwoord is eigenlijk een goed antwoord.

Echter wat wel lijkt te ontbreken, is de mogelijkheid om stukjes code op te roepen zonder iets te matchen. Dit zou – althans volgens sommigen – een veel betere structurering mogelijk maken.

Ik heb een klein voorbeeld gemaakt om te laten zien wat ik bedoel.

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

Na wat gepruts en eerst gebruik gemaakt van de hint dat als er twee overeenkomende sjablonen zijn, de laatste in de code zal worden geselecteerd, en vervolgens mijn code zal herstructureren (niet alle hier getoond), heb ik bereikt dit lijkt te werken, en genereert hopelijk de juiste code, en geeft ook de gewenste gegevens weer –

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

[Scroll de bovenstaande code van boven naar beneden als u niet alles kunt zien]

De manier waarop dit werkt, is dat de hoofdsjabloon altijd overeenkomt – komt overeen met /

Dit heeft de stukjes code – sjablonen – die worden genoemd.

Dit betekent nu dat het niet mogelijk is om een ander sjabloon te matchen op / maar het is wel mogelijk om te matchen
expliciet op een benoemd knooppunt, in dit geval het knooppunt op het hoogste niveau in de xml – genaamde items.

Een kleine wijziging aan de code leverde het bovenstaande voorbeeld op.

Other episodes