Erik - Yellowgrape
Erik - Yellowgrape

Reputation: 1

Why do I see values from another position (xslt)?

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

Answers (2)

Alejandro
Alejandro

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

Mark Schultheiss
Mark Schultheiss

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

Related Questions