Reputation: 1
Sorry for this noob-question, and I solved this already by adding empty templates which is not very elegant, but can anyone help me understand why I kept seeing values from another 'level' in my output?
This is what my xml looks like:
<root>
<offers>
<theme>
<type>theme1</type>
<name>...</name>
<slug>...</slug>
<description>...</description>
<total>32</total>
<url_title>...</url_title>
<url_anchor>...</url_anchor>
<url>...</url>
<offers>
<row>
<offer>
<name>travel</type>
<id>68232</id>
</theme>
<theme>
<type>theme2</type>
<name>...</name>
<slug>...</slug>
<description>...</description>
<total>32</total>
<url_title>...</url_title>
<url_anchor>...</url_anchor>
<url>...</url>
<offers>
<row>
<offer>
<name>clowns</type>
<id>222</id>
</theme>
</offers>
</root>
Each theme only holds 1 offer. I wanted to show the offer from theme/offers/row/offer side by side, so I used:
<xsl:template match="root/offers">
<xsl:apply-templates select="theme[position() mod 2 = 1]" mode="row" />
</xsl:template>
<xsl:template match="theme" mode="row">
<xsl:apply-templates select="offers/row/offer/." mode="offer1" />
<xsl:apply-templates select="following-sibling::theme[1]" mode="offer2" />
</xls:template>
<xsl:template match="*" mode="offer1">
=== This one showed the offer correctly
</template>
<xsl:template match="offer" mode="offer2">
=== This one kept showing the 'type', 'name' 'slug'
=== values from the theme node,
=== even though I didn't ask for them
</xsl:template>
I fixed it simply by adding empty templates, like this:
<xsl:template match="theme/type" mode="offer2"/>
<xsl:template match="theme/name" mode="offer2"/>
<xsl:template match="theme/slug" mode="offer2"/>
I just want to understand where I went wrong. I kept fiddling with the sibling syntax (can not use '.' there...) and I needed to use the mod 2 = 1 at the 'theme' level, since each theme only contained one offer.
Upvotes: 0
Views: 55
Reputation: 1882
The problem arise because of Built-in Template Rules
Let's look this rule:
<xsl:template match="theme" mode="row">
<xsl:apply-templates select="offers/row/offer/." mode="offer1" />
<xsl:apply-templates select="following-sibling::theme[1]" mode="offer2" />
</xls:template>
Do note how you are applying templates selecting offer
grandgrandchild and theme sibling. So, because there is no rule for theme
element in offer2
mode, the built-in rules apply: elements apply node children, text nodes are output.
That is why the transformation (not the rule you say) outputs
the 'type', 'name', 'slug', values from the theme node, even though I didn't ask for them
Update: from OP's comments
Yeah, so how do I lose them, while still pairing them up
Push style: change the rule to
<xsl:template match="theme" mode="row">
<xsl:apply-templates
select="offers/row/offer" mode="offer1" />
<xsl:apply-templates
select="following-sibling::theme[1]/offers/row/offer" mode="offer2" />
</xls:template>
Pull style: add this rule to override text nodes built-in rule in 'offer2'
mode.
<xsl:template match="text()" mode="offer2"/>
Upvotes: 1
Reputation: 34227
You have marked this as XSLT version 1.0 spec here https://www.w3.org/TR/1999/REC-xslt-19991116
If you want the 2.0 that is here https://www.w3.org/TR/2009/PER-xslt20-20090421/
The list can be found here https://www.w3.org/TR/xslt/all/
Your answer lies here with how a copy works in 1.0: https://www.w3.org/TR/1999/REC-xslt-19991116#copying but perhaps a better explanation on the 2.0 spec here https://www.w3.org/TR/xslt20/#copying
This version 1.0 you have can make a difference which might impact the validity of the answers.
OK now that we have that out of the way, let's work with your posted XML. Now I added another offer (might happen in real life right?) so I show how to get all of them and order by id.
Most of the time, I try to simplify the selectors, I show how to do that here, it seems you want to sort offers, so I show that example.
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:html="http://www.w3.org/1999/xhtml"
exclude-result-prefixes="xsl html">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="root/offers">
<!-- made this root up -->
<myoffers>
<!-- since we do not need anything from the theme -->
<xsl:apply-templates select="theme/offers/row/offer">
<!-- or sort by <xsl:sort select="name" data-type="string"/> -->
<xsl:sort select="id" data-type="number"/>
</xsl:apply-templates>
</myoffers>
</xsl:template>
<xsl:template match="row/offer">
<!-- could create new node name here like myoffers -->
<xsl:copy>
<!-- order the elements -->
<xsl:apply-templates select="id" />
<xsl:apply-templates select="name" />
</xsl:copy>
</xsl:template>
<xsl:template match="id | name">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- strip/normalize space, not sure you need but to show how -->
<xsl:template match="text()">
<xsl:value-of select="normalize-space()"/>
</xsl:template>
</xsl:stylesheet>
Example output:
<myoffers> <offer> <id>222</id> <name>clowns</name> </offer> <offer> <id>723</id> <name>burnt meat</name> </offer> <offer> <id>68232</id> <name>travel</name> </offer> </myoffers>
Example input XML I used:
<root>
<offers>
<theme>
<type>theme1</type>
<name>...</name>
<slug>...</slug>
<description>...</description>
<total>32</total>
<url_title>.uuseful..</url_title>
<url_anchor>fun</url_anchor>
<url>...</url>
<offers>
<row>
<offer>
<name>travel</name>
<id>68232</id>
</offer>
</row>
</offers>
</theme>
<theme>
<type>theme2</type>
<name>.namer..</name>
<slug>.slugger..</slug>
<description>.I am me.</description>
<total>32</total>
<url_title>...</url_title>
<url_anchor>...</url_anchor>
<url>...</url>
<offers>
<row>
<offer>
<name>clowns </name>
<id> 222</id>
</offer>
</row>
</offers>
</theme>
<theme>
<type>theme2</type>
<name>.namer..</name>
<slug>.slugger..</slug>
<description>.I am me.</description>
<total>32</total>
<url_title>...</url_title>
<url_anchor>...</url_anchor>
<url>...</url>
<offers>
<row>
<offer>
<name>burnt meat</name>
<id> 723</id>
</offer>
</row>
</offers>
</theme>
</offers>
</root>
You can test that all if you paste it here http://fiddle.frameless.io/
Upvotes: 0