Processing stacks of parameters using 'for-eac

2019-03-05 23:46发布

I have a series of param elements inside a template that rely upon each other (i.e. each subsequent param uses the value of the previous one as part of it's XPath)

e.g.

<xsl:param name="input1" select="path/node/@value"/>

<xsl:param name="input2" select="path/anothernode[@value=$input1]/anothervalue"/>

<xsl:param name="input3" select="path/thirdnode[@value=$input2]/@endvalue"/>

etc. etc.

The stack of params works perfectly on a single run through. However, when I use a for-each loop to call the template and pass the for-each value to it using xsl:with-param, only the top param is processed - the value is NOT passed to the next the parameter.

Here's my full xsl:

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

<xsl:template match="/" name="test">

  <xsl:param name="memberId"    
    select="PLMXML/Organisation/UserData/UserValue/UserList/Item/@value"/>
  <xsl:param name="memberRef" 
    select="PLMXML/OrganisationMember[@id=$memberId]/@memberRef"/>  
  <xsl:param name="memberRef2"    
    select="translate($memberRef,'#','')"/> 
  <xsl:param name="userId"    
    select="PLMXML/User[@id=$memberRef2]/@personRef"/>
  <xsl:param name="userId2"    
    select="translate($userId,'#','')"/>    
  <xsl:param name="personName"    
    select="PLMXML/Person[@id=$userId2]/@lastName"/>
  <xsl:param name="groupRef"    
    select="PLMXML/User[@id=$memberRef2]/UserData/UserValue/@dataRef"/>
  <xsl:param name="groupRef2"    
    select="translate($groupRef,'#','')"/>
  <xsl:param name="groupName"    
    select="PLMXML/Organisation[@id=$groupRef2]/@name"/>
  <xsl:param name="roleRef"    
    select="PLMXML/Organisation[@id=$groupRef2]/UserData/UserValue/@dataRef"/>
  <xsl:param name="roleRef2"    
    select="translate($roleRef,'#','')"/>
  <xsl:param name="roleName"    
    select="PLMXML/Role[@id=$roleRef2]/@name"/>

  <xsl:value-of select="$memberId"/>

  <xsl:value-of select="$memberRef"/>

  <xsl:value-of select="$personName"/>  
  <xsl:value-of select="$groupName"/>
  <xsl:value-of select="$roleName"/>

</xsl:template>

<xsl:template match="/">

  <xsl:for-each select="PLMXML/Organisation/UserData/UserValue/UserList/Item/@value">

    <xsl:call-template name="test">

      <xsl:with-param name="memberId" select="."/>

    </xsl:call-template>

  </xsl:for-each>

</xsl:template>


</xsl:stylesheet>

However, the for-each loop only populates the top variable of the stack in template 'test' - i.e. xsl:value-of select="$memberId"/> works but <xsl:value-of select="$memberRef"/> does not.

Any ideas on how to work around this issue would be much appreciated.

1条回答
迷人小祖宗
2楼-- · 2019-03-06 00:28

The problem you are having is one of context; where you are currently positioned in the XML. Consider your xsl:for-each loop

<xsl:for-each select="PLMXML/Organisation/UserData/UserValue/UserList/Item/@value">

You are looping over @value attributes, meaning within the loop you are positioned on a @value attribute, and consequently any relative xpath expressions will be relative to that. When you do your xsl:call-template you will still be positioned on the @value attribute, and so all the xpath expressions in the xsl:param statements will be relative to that.

<xsl:param name="memberRef" 
     select="PLMXML/OrganisationMember[@id=$memberId]/@memberRef"/>  

That is to say, the select statement is looking for a PLMXML element underneath the current @value, which clearly does not exist!

The reason the first parameter, memberId, works is because you pass an explicit value to that, and so that will be used instead of the default value in the select attribute. All the other parameters will not work because they will use the default value.

The solution is, probably, is to make all your parameters have absolute expressions, which means prefixing them with a /.

<xsl:param name="memberRef" 
     select="/PLMXML/OrganisationMember[@id=$memberId]/@memberRef"/> 

The / represents the top level document node, and so the xpath expression will then be relative to that, and not the current node.

Note, you might consider using xsl:key in this instance to look-up such elements. For example

<xsl:key name="OrganisationMember" match="OrganisationMember" use="@id" />

Then the param statement becomes this:

<xsl:param name="memberRef" select="key('OrganisationMember', $memberId)/@memberRef"/> 

As an aside, it is considered an error to have two templates of equal priority matching the same thing. So, your first 'named' template should probably have the match attribute removed. i.e. Instead of <xsl:template match="/" name="test">, do this

 <xsl:template name="test">
查看更多
登录 后发表回答