user458117
user458117

Reputation: 81

Why does XSLT template not get applied?

I have some XML that represents two alternative versions of some text and I am using XSLT to perform the modification that selects one or other versions. In addition, one alternative also contains a placeholder that should be replaced with some other bit of text.

The XSLT is generated programmatically and is essentially an identity transformation with a couple of extra templates that perform the necessary tweaks. However, when the XSLT matches the alternative with the placeholder, the placeholder template doesn't get applied and is never filled in.

The actual code uses python's lxml, but I have been mostly testing the XSLT in notepad++ with the XML plugin. The plugin uses libxml2 and libxslt, like lxml, so they shouldn't be any different.

I have tried adding various versions of <xsl:apply-template /> into template 2, but nothing results in the placeholder getting the value that I expect

The following is a very cut down version of the XML:

<sn:text xmlns:sn="http://some.namespace" xmlns="http://www.w3.org/1999/xhtml">
  <p>
    <sn:alts sn:id="alts_1">
      <sn:alt sn:id="alt_1">start 1</sn:alt>
      <sn:alt sn:id="alt_2">
        <sn:placeholder sn:id="p_1"/> start 2</sn:alt>
    </sn:alts> blah blah blah...</p>
</sn:text>

And the following is the generated XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" xmlns:sn="http://some.namespace">
  <!--template 1-->
  <xsl:template match="sn:placeholder[@sn:id='p_1']">XYZ</xsl:template>
  <!--template 2-->
  <xsl:template match="sn:alts[@sn:id='alts_1']">
    <xsl:value-of select="sn:alt[@sn:id='alt_2']" />
  </xsl:template>
  <!--identity transform-->
  <xsl:template match="*">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="@*|text()|comment()|processing-instruction">
    <xsl:copy-of select="."/>
  </xsl:template>
</xsl:stylesheet>

The result that I am expecting should be something like:

<sn:text xmlns:sn="http://some.namespace" xmlns="http://www.w3.org/1999/xhtml">
  <p>XYZ start 2 blah blah blah...</p>
</sn:text>

Instead, the placeholder is not replaced with "XYZ", but is left out completely:

<sn:text xmlns:sn="http://some.namespace" xmlns="http://www.w3.org/1999/xhtml">
  <p> start 2 blah blah blah...</p>
</sn:text>

Update

Thanks for the pointers. It was quite obvious in the end:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" xmlns:sn="http://some.namespace">
  <xsl:strip-space elements="*"/>
  <xsl:output method="xml" indent="yes" />
  <!--template 1-->
  <xsl:template match="sn:placeholder[@sn:id='p_1']">XYZ</xsl:template>
  <!--template 2-->
  <xsl:template match="sn:alts[@sn:id='alts_1']">
    <xsl:apply-templates select="sn:alt/sn:placeholder"/>
    <xsl:value-of select="sn:alt[@sn:id='alt_1']" />
  </xsl:template>
  <!--identity transform-->
  <xsl:template match="*">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="@*|text()|comment()|processing-instruction()">
    <xsl:copy-of select="."/>
  </xsl:template>
</xsl:stylesheet>

Upvotes: 1

Views: 435

Answers (3)

Tim C
Tim C

Reputation: 70648

Is the logic you are trying to implement as follows:

  1. If "alt_2" exists then output that (with a modification for the placeholder), and ignore "alt_1"
  2. Otherwise output "alt_1"

In that case, try this XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" xmlns:sn="http://some.namespace">

  <!--template 1-->
  <xsl:template match="sn:placeholder[@sn:id='p_1']">XYZ</xsl:template>

  <!--template 2-->
  <xsl:template match="sn:alts[sn:alt[@sn:id='alt_2']]/sn:alt[@sn:id='alt_1']" />

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

  <!--identity transform-->
  <xsl:template match="*">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="@*|text()|comment()|processing-instruction">
    <xsl:copy-of select="."/>
  </xsl:template>
</xsl:stylesheet>

See http://xsltfiddle.liberty-development.net/ncdD7mo

Upvotes: 0

imran
imran

Reputation: 461

<xsl:stylesheet version = "2.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" xmlns:sn="http://some.namespace" xmlns:html="http://www.w3.org/1999/xhtml">
    <xsl:strip-space elements="*"/>
    <xsl:output method = "xml" indent = "yes" />
    <xsl:template match="sn:text">
        <xsl:copy>
        <p>
        <xsl:apply-templates select="html:p/sn:alts/sn:alt[@sn:id = 'alt_2']"/>
        <xsl:apply-templates select="html:p/text()"/>
        </p>
   </xsl:copy>
    </xsl:template>

    <xsl:template match="sn:alt[@sn:id = 'alt_2']">
        <xsl:text>xyz </xsl:text><xsl:value-of select="."/><xsl:text> </xsl:text>
    </xsl:template>

</xsl:stylesheet>
You may try it.

Upvotes: 0

Michael Kay
Michael Kay

Reputation: 163675

The template isn't applied because you never select the node that it matches in an xsl:apply-templates instruction. That's because your match="sn:alts" doesn't apply-templates to its children.

Incidentally, processing-instruction should be processing-instruction().

Upvotes: 1

Related Questions