smj100
smj100

Reputation: 15

Change parent attribute value based on child value

I have to change the attribute of a parent node based on a combination of parent attirbute value and and child value. My input xml is as below:

<filters>
<sheetFilter filterUsage="table"  labelKey="WR" hidden="false">
    <userLogin>U0002</userLogin>
    <containers>
          <sheetTypeRef name="WorkRequest"/>
    </containers>
</sheetFilter>
<sheetFilter filterUsage="table"  labelKey="AR" hidden="false">
    <userLogin>U0003</userLogin>
    <containers>
          <sheetTypeRef name="ARRequest"/>
    </containers>
</sheetFilter>
<sheetFilter filterUsage="table"  labelKey="WR" hidden="false">
    <userLogin>U0002</userLogin>
    <containers>
          <sheetTypeRef name="WorkRequest"/>
    </containers>
</sheetFilter>
</filters>

In case of @labelKey and userLogin value combination is repeating, I have to replace the @labelKey with a counter appeneded and the desired output should be like:

<filters>    
    <sheetFilter filterUsage="table" labelKey="WR1" hidden="false">
        <userLogin>U0002</userLogin>
        <containers>
            <sheetTypeRef name="WorkRequest"/>
        </containers>
    </sheetFilter>
    <sheetFilter filterUsage="table" labelKey="AR" hidden="false">
        <userLogin>U0003</userLogin>
        <containers>
            <sheetTypeRef name="WorkRequest"/>
        </containers>
    </sheetFilter>
    <sheetFilter filterUsage="table" labelKey="WR2" hidden="false">
        <userLogin>U0002</userLogin>
        <containers>
            <sheetTypeRef name="WorkRequest"/>
        </containers>
    </sheetFilter>
</filters>

I have written the below xslt to apply the transformation:

<xsl:template match="*|@*">
    <xsl:copy disable-output-escaping="yes">
    <xsl:apply-templates select="@*"/>
    <xsl:apply-templates select="node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="sheetFilter[@labelKey='WR']">
    <xsl:param name="i" select="1" />
    <xsl:element name="{ local-name() }" disable-output-escaping="yes">

    <xsl:for-each select="userLogin[text()='U0002']" >  
        <xsl:with-param name="i" select="$i +1"/>
        <xsl:if test="not(../sheetFilter[@labelKey])">
        <xsl:apply-templates select="../@*"/>
        </xsl:if>

        <xsl:attribute name="labelKey">
        <xsl:value-of select="concat('WR', $i)"/>
        </xsl:attribute>
    </xsl:for-each>
    <xsl:apply-templates select="node()"/>
    </xsl:element>
</xsl:template>

But I am getting is a compiler warning below:

Compiler warnings:
Attribute 'labelKey' outside of element

And the output is not consistent, in some nodes the counter value is applied to 'labelKey' and in some nodes @lableKey itself is missing. Can anybody throw some light what went wrong in the above xslt? Also I would like to know is it possible to generalise the condition, for example match="sheetFilter[@labelKey='WR']" is it possible to replace the WR and U0002 with a generalised statement since I am not sure of the combination values that may repeat.

Thanks in advance. Susan

Upvotes: 0

Views: 1956

Answers (3)

hr_117
hr_117

Reputation: 9627

Only for book keeping: Here a solution which keeps order and does work without for-each.

   version="1.0">
    <xsl:output method="xml" indent="yes"/>
    <xsl:key name="kSheetFilter" match="sheetFilter " use="concat(@labelKey,'#',userLogin )" />

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

    <xsl:template match="sheetFilter" >
        <xsl:param name="pos" />
        <xsl:param name="copy_cnt"/>
        <xsl:copy>
            <xsl:apply-templates select="@* "/>
            <xsl:variable name="sheets" select="key( 'kSheetFilter',concat(@labelKey,'#',userLogin ))" />
            <xsl:if test="count($sheets) &gt; '1'">
                <xsl:attribute name="labelKey" >
                    <xsl:value-of select="concat(@labelKey, 
                                  count(preceding-sibling::sheetFilter
                                  [ 
                                    @labelKey = current()/@labelKey and
                                    userLogin = current()/userLogin 
                                  ]) +1
                                  )"/>
                </xsl:attribute>
            </xsl:if>
            <xsl:apply-templates select="node()"/>
        </xsl:copy>

    </xsl:template>

</xsl:stylesheet>

Upvotes: 0

hr_117
hr_117

Reputation: 9627

If the original order of sheetFilter elements does not mater, you can use something based on MUENCHIAN METHOD. This will also work with xlt-1.0.

Try this:

<?xml version="1.0"?>
<xsl:stylesheet
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   version="1.0">
    <xsl:output method="xml" indent="yes"/>
    <xsl:key name="kSheetFilter" match="sheetFilter " use="concat(@labelKey,'#',userLogin )" />

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

    <xsl:template match="sheetFilter" >
        <xsl:param name="pos" />
        <xsl:param name="copy_cnt"/>
        <xsl:copy>
            <xsl:apply-templates select="@* "/>
            <xsl:if test="$copy_cnt &gt; '1'">
                <xsl:attribute name="labelKey" >
                    <xsl:value-of select="concat(@labelKey, $pos)"/>
                </xsl:attribute>
            </xsl:if>
            <xsl:apply-templates select="node()"/>
        </xsl:copy>

    </xsl:template>
    <xsl:template match="/*">
        <xsl:copy>
            <xsl:for-each
                select="sheetFilter[ generate-id()= 
            generate-id( key( 'kSheetFilter',   concat(@labelKey,'#',userLogin ) 
                   ) [1])]"  >
                <xsl:variable name="sheets" select="key( 'kSheetFilter',    concat(@labelKey,'#',userLogin ))" />
                <xsl:for-each select=" $sheets" >
                <xsl:apply-templates select=".">
                    <xsl:with-param name="pos" select="position()"/>
                    <xsl:with-param name ="copy_cnt" select="count($sheets)" />
                </xsl:apply-templates>
                </xsl:for-each>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

This will generate the following output:

<filters>
  <sheetFilter filterUsage="table" labelKey="WR1" hidden="false">
                <userLogin>U0002</userLogin>
                <containers>
                        <sheetTypeRef name="WorkRequest"/>
                </containers>
        </sheetFilter>
  <sheetFilter filterUsage="table" labelKey="WR2" hidden="false">
                <userLogin>U0002</userLogin>
                <containers>
                        <sheetTypeRef name="WorkRequest"/>
                </containers>
        </sheetFilter>
  <sheetFilter filterUsage="table" labelKey="AR" hidden="false">
                <userLogin>U0003</userLogin>
                <containers>
                        <sheetTypeRef name="ARRequest"/>
                </containers>
        </sheetFilter>
</filters>

Upvotes: 0

Navin Rawat
Navin Rawat

Reputation: 3138

Your shared XSLT is not good. I have create new XSLT to get your desired output:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>

  <xsl:template match="filters">
    <filters>
      <xsl:call-template name="abc">
        <xsl:with-param name="filter" select="."/>
      </xsl:call-template>
    </filters>
  </xsl:template>

  <xsl:template name="abc" match="//sheetFilter">
    <xsl:param name="filter"/>
    <xsl:for-each select="$filter/child::*">
      <xsl:variable name="labelKey" select="@labelKey"/>
      <xsl:variable name="userLogin" select="userLogin"/>
      <sheetFilter filterUsage="{@filterUsage}"
        labelKey="{if ( count(//sheetFilter[@labelKey = $labelKey][userLogin = $userLogin]) gt 1)
                  then concat(@labelKey,sum(count(preceding-sibling::sheetFilter[@labelKey=$labelKey])+1))
                  else @labelKey
                  }"
        hidden="{@hidden}">
        <xsl:copy-of select="child::*"/>
      </sheetFilter>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

SAMPLE1 XML:

<filters>
  <sheetFilter filterUsage="table" labelKey="WR" hidden="false">
    <userLogin>U0002</userLogin>
    <containers>
      <sheetTypeRef name="WorkRequest"/>
    </containers>
  </sheetFilter>
  <sheetFilter filterUsage="table" labelKey="AR" hidden="false">
    <userLogin>U0003</userLogin>
    <containers>
      <sheetTypeRef name="ARRequest"/>
    </containers>
  </sheetFilter>
  <sheetFilter filterUsage="table" labelKey="WR" hidden="false">
    <userLogin>U0002</userLogin>
    <containers>
      <sheetTypeRef name="WorkRequest"/>
    </containers>
  </sheetFilter>
</filters>

OUTPUT:

<filters>
   <sheetFilter filterUsage="table" labelKey="WR1" hidden="false">
      <userLogin>U0002</userLogin>
      <containers>
         <sheetTypeRef name="WorkRequest"/>
      </containers>
   </sheetFilter>
   <sheetFilter filterUsage="table" labelKey="AR" hidden="false">
      <userLogin>U0003</userLogin>
      <containers>
         <sheetTypeRef name="ARRequest"/>
      </containers>
   </sheetFilter>
   <sheetFilter filterUsage="table" labelKey="WR2" hidden="false">
      <userLogin>U0002</userLogin>
      <containers>
         <sheetTypeRef name="WorkRequest"/>
      </containers>
   </sheetFilter>
</filters>

SAMPLE2 XML:

<filters>
  <sheetFilter filterUsage="table" labelKey="WR" hidden="false">
    <userLogin>111U0002</userLogin>
    <containers>
      <sheetTypeRef name="WorkRequest"/>
    </containers>
  </sheetFilter>
  <sheetFilter filterUsage="table" labelKey="AR" hidden="false">
    <userLogin>U0003</userLogin>
    <containers>
      <sheetTypeRef name="ARRequest"/>
    </containers>
  </sheetFilter>
  <sheetFilter filterUsage="table" labelKey="WR" hidden="false">
    <userLogin>U0002</userLogin>
    <containers>
      <sheetTypeRef name="WorkRequest"/>
    </containers>
  </sheetFilter>
</filters>

OUTPUT:

<?xml version="1.0" encoding="UTF-8"?>
<filters>
   <sheetFilter filterUsage="table" labelKey="WR" hidden="false">
      <userLogin>111U0002</userLogin>
      <containers>
         <sheetTypeRef name="WorkRequest"/>
      </containers>
   </sheetFilter>
   <sheetFilter filterUsage="table" labelKey="AR" hidden="false">
      <userLogin>U0003</userLogin>
      <containers>
         <sheetTypeRef name="ARRequest"/>
      </containers>
   </sheetFilter>
   <sheetFilter filterUsage="table" labelKey="WR" hidden="false">
      <userLogin>U0002</userLogin>
      <containers>
         <sheetTypeRef name="WorkRequest"/>
      </containers>
   </sheetFilter>
</filters>

SAMPLE3 XML:

<filters>
  <sheetFilter filterUsage="table" labelKey="" hidden="false">
    <userLogin>U0002</userLogin>
    <containers>
      <sheetTypeRef name="WorkRequest"/>
    </containers>
  </sheetFilter>
  <sheetFilter filterUsage="table" labelKey="AR" hidden="false">
    <userLogin>U0003</userLogin>
    <containers>
      <sheetTypeRef name="ARRequest"/>
    </containers>
  </sheetFilter>
  <sheetFilter filterUsage="table" labelKey="WR" hidden="false">
    <userLogin>U0002</userLogin>
    <containers>
      <sheetTypeRef name="WorkRequest"/>
    </containers>
  </sheetFilter>
</filters>

OUTPUT:

<?xml version="1.0" encoding="UTF-8"?>
<filters>
   <sheetFilter filterUsage="table" labelKey="" hidden="false">
      <userLogin>U0002</userLogin>
      <containers>
         <sheetTypeRef name="WorkRequest"/>
      </containers>
   </sheetFilter>
   <sheetFilter filterUsage="table" labelKey="AR" hidden="false">
      <userLogin>U0003</userLogin>
      <containers>
         <sheetTypeRef name="ARRequest"/>
      </containers>
   </sheetFilter>
   <sheetFilter filterUsage="table" labelKey="WR" hidden="false">
      <userLogin>U0002</userLogin>
      <containers>
         <sheetTypeRef name="WorkRequest"/>
      </containers>
   </sheetFilter>
</filters>

Upvotes: 0

Related Questions