Search code examples
xsltdynamicconditional-statementsapply-templates

xslt dynamic / conditional apply-template in function of a variable?


I want to display two different XSLT transformations in function of what the user want. The entire XSL file is the same, except for one line.

This line should be as it

<xsl:template match="/data/peptides/peptide[generate-id()=generate-id(key('byAccSeq', concat(protein_accessions/accession, '|', sequence))[1])]">

or as it

<xsl:template match="/data/peptides/peptide">

My first idea was to create two different .xsl files, and to apply them (javascript) in function of a variable value.

var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(xslDoc);
xhtml = xsltProcessor.transformToFragment(xmlDoc,document);

However, it is just a line and I would like to maintain only one file. I would like to do something like this

<xsl:param name="variable"/>
<xsl:choose>
<xsl:when test="$variable = 0">
<xsl:template match="/data/peptides/peptide[generate-id()=generate-id(key('byAccSeq', concat(protein_accessions/accession, '|', sequence))[1])]">
...
</xsl:template>
</xsl:when>
<xsl:otherwise>
<xsl:template match="/data/peptides/peptide">
...
</xsl:template>
</xsl:otherwise>
</xsl:choose>

But it does not work.

trying with the feature "mode" in the xsl:apply-templates, this code neither works

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="byAccSeq" match="/data/peptides/peptide" use="concat(accession, '|', sequence)"/>

<xsl:param name="analysis" select="1"/>

<xsl:template match="/">
<root><name><xsl:value-of select="$analysis"/></name><xsl:apply-templates select="/data/proteins/protein"/></root>
</xsl:template>

<xsl:template match="/data/proteins/protein">
<xsl:apply-templates select="/data/peptides/peptide[accession=current()/accession]"/>
</xsl:template>

<xsl:choose>
<xsl:when test="$analysis=1">
<xsl:apply-templates select="/data/peptides/peptide" mode="one"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="/data/peptides/peptide[accession=current()/accession]" mode="two"/>
</xsl:otherwise>
</xsl:choose>

<xsl:template match="/data/peptides/peptide" mode="one">
<xsl:copy-of select="."/>
</xsl:template>

<xsl:template match="/data/peptides/peptide[generate-id()=
           generate-id(key('byAccSeq', concat(accession, '|', sequence))[1])]" mode="two">
<xsl:copy-of select="."/>
</xsl:template>

</xsl:stylesheet>

-> http://www.xsltcake.com/slices/sgWUFu/2

  • this code is not correct since xsl:choose cannot be a child of xsl:stylesheet

SOLVED (improved below), this is the code that makes what I wanted

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
    <xsl:key name="byAccSeq" match="/data/peptides/peptide" use="concat(accession, '|', sequence)"/>
    <xsl:param name="analysis" select="1"/>
    <xsl:template match="/">
        <root>
            <name>
                <xsl:value-of select="$analysis"/>
            </name>
            <xsl:apply-templates select="/data/proteins/protein"/>
        </root>
    </xsl:template>
    <xsl:template match="/data/proteins/protein">
        <xsl:choose>
            <xsl:when test="$analysis=1">
                <xsl:apply-templates select="/data/peptides/peptide[accession=current()/accession]" mode="one"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates select="/data/peptides/peptide[accession=current()/accession]" mode="two"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template match="/data/peptides/peptide" mode="one">
        <xsl:copy-of select="."/>
    </xsl:template>

    <xsl:template match="/data/peptides/peptide" mode="two"/>

    <xsl:template match="/data/peptides/peptide[generate-id()=
    generate-id(key('byAccSeq', concat(accession, '|', sequence)))]" mode="two">
    <xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>

IMPROVED: the final code is much easier to read with much less duplicated code here

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
    <xsl:param name="analysis" select="0"/>
    <xsl:key name="byAcc"    match="/data/peptides/peptide" use="accession" />
    <xsl:key name="byAccSeq" match="/data/peptides/peptide" use="concat(accession, '|', sequence)"/>
    <xsl:template match="/">
        <root>
            <name>
                <xsl:value-of select="$analysis"/>
            </name>
            <xsl:apply-templates select="/data/proteins/protein" />
        </root>
    </xsl:template>
    <xsl:template match="/data/proteins/protein">
        <xsl:choose>
            <xsl:when test="$analysis=1">
                <xsl:apply-templates select="key('byAcc',accession)" />
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates select="key('byAcc',accession)[
                generate-id()
                =
                generate-id(key('byAccSeq', concat(accession, '|', sequence)))]" />
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template match="/data/peptides/peptide">
        <xsl:copy-of select="."/>
    </xsl:template>
</xsl:stylesheet>

I keep the apply-template calls because I need it, but without them (as in the original code, see comments) is even simpler.

Thanks again, not only for answering but for teaching XSLT :)


Solution

  • /data/peptides/peptide[
      generate-id()
      =
      generate-id(
        key('byAccSeq', concat(protein_accessions/accession, '|', sequence))[1]
      )
    ]
    

    and

    /data/peptides/peptide[
      generate-id()
      =
      generate-id(
        key('byAccSeq', concat(protein_accessions/accession, '|', sequence))
      )
    ]
    

    are equivalent. generate-id() always returns the ID of the first node in the node-set that has been passed to it.

    Therefore it does not make any difference whether you use generate-id(some-node-set) or generate-id(some-node-set[1]). You can use either one of the XSLT files.