XSLT: nested for-each and dynamic variable

2019-09-01 03:15发布

This is my XML and XSLT code

<root>
    <act>
        <acts id>123</acts>
    </act>
    <comp>
        <comps id>233</comps>
    </comp>
</root>


<xsl:for-each select="act/acts">
    <xsl:variable name="contactid" select="@id"/>
    <xsl:for-each select="root/comp/comps">
        <xsl:variable name="var" select="boolean(contactid=@id)"/>
    </xsl:for-each>
    <xsl:choose>
        <xsl:when test="$var='true'">
          . . . do this . . .
        </xsl:when>
        <xsl:otherwise>
          . . . do that . . .
        </xsl:otherwise>
    </xsl:choose>
</xsl:for-each>

I want to dynamically assign true or false to var and use it inside <xsl:choose> for boolean test. I hope this helps to find a better solution to get rid of for-each also

标签: xslt
2条回答
一纸荒年 Trace。
2楼-- · 2019-09-01 03:17

There are some errors in your xml file, but assuming what you mean is:

<root>
    <act><acts id="123"></acts></act>
    <comp><comps id="233"></comps></comp>
</root>

Here is a full solution:

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

    <xsl:template match="/">
        <doc>   
           <xsl:apply-templates select="root/comp/comps"/>  
        </doc>
    </xsl:template>

    <xsl:template match="root/comp/comps"> 
        <xsl:variable name="compsid" select="@id"></xsl:variable>
        <xsl:choose>
            <xsl:when test="count(/root/act/acts[@id=$compsid])&gt;0">Do This</xsl:when>
            <xsl:otherwise>Do That</xsl:otherwise>
        </xsl:choose>
    </xsl:template> 

</xsl:stylesheet>
查看更多
成全新的幸福
3楼-- · 2019-09-01 03:20

First thing to note is that variables in XSLT are immutable, and cannot be changed once initialised. The main problem with your XSLT is that you define your variable within an xsl:for-each block and so it only exists within the scope of that block. It is not a global variable. A new variable gets defined each time that can only be used within the xsl:for-each

From looking at your XSLT it looks like you want to iterate over the acts element and perform a certain action depending on whether an comps element exists with the same value. An alternative approach would be to define a key to look up the comps elements, like so

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

Then you can simply check whether a comps element exists like so (assuming you are positioned on an acts element.

<xsl:choose>
   <xsl:when test="key('comps', @id)">Yes</xsl:when>
   <xsl:otherwise>No</xsl:otherwise>
</xsl:choose>

Here is the full XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:key name="comps" match="comps" use="@id" />

   <xsl:template match="/root">
      <xsl:apply-templates select="act/acts" />
   </xsl:template>

   <xsl:template match="acts">
      <xsl:choose>
         <xsl:when test="key('comps', @id)"><res>Yes</res></xsl:when>
         <xsl:otherwise><res>No</res></xsl:otherwise>
      </xsl:choose>
   </xsl:template>
</xsl:stylesheet>

When applied to the following (well-formed) XML

<root>
   <act>
      <acts id="123"/>
   </act>
   <comp>
      <comps id="233"/>
   </comp>
</root>

The following is output

No

However, it can often be preferably in XSLT to avoid the use of conditional statements like xsl:choose and xsl:if. Instead, you can structure the XSLT to make use of template matching. Here is the alternate approach

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:key name="comps" match="comps" use="@id" />

   <xsl:template match="/root">
      <xsl:apply-templates select="act/acts" />
   </xsl:template>

   <xsl:template match="acts[key('comps', @id)]">
      <res>Yes</res>
   </xsl:template>

   <xsl:template match="acts">
      <res>No</res>
   </xsl:template>
</xsl:stylesheet>

When applied to the same XML, the same result is output. Do note the more specific template for the acts node will take priority when matching the case where a comps exist.

查看更多
登录 后发表回答