xsltxslt-1.0xsl-variable

XSLT 1.0 cannot select variables in xsl:value-of select


I try to transform a piece of XML that is generated by multiple sources. If I enter the text directly into the value-of select statement it works as intended. But as soon as I try to use variables it won't work any more.

If I use variables in the xsl:for-each statement it works fine too. This is the XSL that works fine

<xsl:variable name="crm_acc" select="account" />
<xsl:variable name="nav_acc" select="kontakt" />
<xsl:variable name="crm_fname" select="firstname" />
<xsl:variable name="nav_fname" select="fname" />

<TreeView>
  <xsl:for-each select="$crm_acc | $nav_acc">
    <TreeViewItem Header="Item">
      <TreeViewItem Header="Firstname:" >
        <xsl:value-of select="firstname | fname" />
      </TreeViewItem>
    </TreeViewItem>
  </xsl:for-each>
</TreeView>

But if I use variables inside of xsl:value-of select it just doesn't work as specified in the next code block. I tried a lot of combinations e.g. using only one variable with "firstname", "firstname | fname", trying to concat(...) the text etc...

<TreeView>
  <xsl:for-each select="$crm_acc | $nav_acc">
    <TreeViewItem Header="Item">
      <TreeViewItem Header="Firstname:" >
        <xsl:value-of select="$crm_fname | $nav_fname" />
      </TreeViewItem>
    </TreeViewItem>
  </xsl:for-each>
</TreeView>

Input XML:

<?xml version='1.0'?>
<root>
  <account system="CRM">
    <firstname>test1</firstname>
  </account>
  <account system="CRM">
    <firstname>test2</firstname>
  </account>
  <kontakt system="NAV">
    <erstername>nav1</erstername>
  </kontakt>
</root>

Only once I got a result, as I specified "account/firstname" in the variable. But this way I always got the value of the first element only in every for-each iteration. It seems as if it looses the context when specifying variables (that the processor is currently inside an "account" entity and should select "firstname as a subnode).

I read that something changed in the value-of select statement from XSLT 1.0 to 2.0 but I do not fully understand what the difference is (I am pretty new to XSLT).

How do I have to specify the XSL to get it to work?

Update: The expected output as in code block 1

<?xml version="1.0" encoding="UTF-8"?>
<TabItem xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Header="Xaml Accounts Tab">
  <TreeView>
    <TreeViewItem Header="CRM">
       <TreeViewItem Header="Firstname:">test1</TreeViewItem>
    </TreeViewItem>
    <TreeViewItem Header="CRM">
       <TreeViewItem Header="Firstname:">test2</TreeViewItem>
    </TreeViewItem>
    <TreeViewItem Header="NAV">
       <TreeViewItem Header="Firstname:">nav1</TreeViewItem>
    </TreeViewItem>
  </TreeView>
</TabItem>

Edit: What I should have mentioned is that these template(s) will/can get modified so it would be easier if all the "variables" are in one place. It could be that the target is not named "contact" but something else and changing it in the whole document is error prone and redundant. Also these variables are used in another process read by a XmlReader. I try to keep hassle of configuration and redundancy as low as possible.


Solution

  • Here are two possible approaches you might want to explore: one uses a variable to store all possible names of an element you might wish to call upon; the other assumes that the position of an element within its parent is known, therefore the name is of no importance.

    The following stylesheet shows both methods, the former for the parent element, the latter for the firstname child.

    <?xml version="1.0" encoding="utf-8"?>
    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:exsl="http://exslt.org/common"
    extension-element-prefixes="exsl">
    <xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
    
    <xsl:variable name="CRM">
        <name>account</name>
        <name>kontakt</name>
    </xsl:variable>
    
    <xsl:template match="/">
    <TreeView>
        <xsl:for-each select="root/*[local-name()=exsl:node-set($CRM)/name]">
            <TreeViewItem Header="{@system}">      
                <TreeViewItem Header="Firstname:" >
                    <xsl:value-of select="*[1]" />
                </TreeViewItem>
            </TreeViewItem>
      </xsl:for-each>
    </TreeView>
    
    </xsl:template>
    </xsl:stylesheet>