user2842773
user2842773

Reputation: 77

XSL transformation when element contains value

I winded up doing some xsl transformations on .xml, I managed to do somewhat of what I would like to do, but I can't figure out the rest. This is the .xml content

  <Parent1>
    <Parent2>
        <Number>"100"</Number>
        <Name>"SanJose"</Name>
        <Type>"SanJoseExtra"</Type>
        <Adress>Avenue 54</Adress>
        <Status>2</Status>
        <TR5>1</TR5>
        <TR10>0</TR10>
        <Modifier>0</Modifier>
    </Parent2>
    <Parent2>
        <Number>"100"</Number>
        <Name>"SanTropez"</Name>
        <Type>"SanTropezSmall"</Type>
        <Adress>British Cal 3</Adress>
        <Status>2</Status>
        <TR5>1</TR5>
        <TR10>1</TR10>
        <Modifier>0</Modifier>
    </Parent2>
    <Parent2>
        <Number>"101"</Number>
        <Name>"SanJose"</Name>
        <Type>"SanDiegoMedium"</Type>
        <Adress>French Revolution n.n.</Adress>
        <Status>2</Status>
        <TR5>1</TR5>
        <TR10>1</TR10>
        <Modifier>0</Modifier>
    </Parent2>
    <Parent2>
        <Number>"100"</Number>
        <Name>"SanJose"</Name>
        <Type>"SanJoseSmall"</Type>
        <Adress>Avenue 54</Adress>
        <Status>1</Status>
        <TR5>1</TR5>
        <TR10>0</TR10>
        <Modifier>0</Modifier>
    </Parent2>
</Parent1>

and I would like to change the Modifier value to 1 if the <Name> contains SanJose and the <Status> contains 1. So I made something like this to get this output :

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



    <xsl:template match="XMLLine[contains(./Name,'SanJose') and contains(./Status,'1')]"> 
        <Parent2>
                        <xsl:copy-of select="Number" />
                        <xsl:copy-of select="Name" />
                        <xsl:copy-of select="Type" />
                        <xsl:copy-of select="Adress" />
                        <xsl:copy-of select="Status" />
                        <xsl:copy-of select="TR5" />
                        <xsl:copy-of select="TR10" />
                        <Modifier>1</Modifier>
        </Parent2> 
        </xsl:template>
    </xsl:stylesheet>

But then I realized that there is SanJose with status 1 where I also want to change the Modifier to 1, but I don't have anything connecting it other than Name which I know in advance and the 100 which I don't always know in advance because the list goes on.

So to sum it up, the problem I find is that I have common name and status which I can use but I also want to apply it to other nodes containing name SanJose but with the same Number which I can't know in advance.

EDIT: Adding an increment for every occurrence with the same Number. So instead of changing the Modifier to 1 I was trying to make it STATUS1, STATUS2, STATUS3.... and so on. I tried modifying your xsl:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:variable name="incre">0</xsl:variable>

<xsl:key name="matching-parent" match="Parent2[Name='SanJose' and Status=1]" use="Number" />

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

<xsl:template match="Modifier[key('matching-parent', ../Number)]/text()"> 
<xsl:value-of select="concat('STATUS', $incre+1)" /> 
</xsl:template> 

</xsl:stylesheet>

However, my logic with the $incre fails, because it never increments, it always remains STATUS1.

Upvotes: 1

Views: 241

Answers (2)

michael.hor257k
michael.hor257k

Reputation: 116959

first change all the nodes where <name> = SanJose and <status> = 1, and then take the <Number> from such cases and change it everywhere else where that <Number> occurs

If I understand correctly, this could also be expressed as:

  1. group all nodes by Number;
  2. if any member of a group satisfies [Name='SanJose' and Status='1'], then change the entire group.

Looking at it this way, you could streamline the solution to:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:key name="matching-parent" match="Parent2[Name='SanJose' and Status=1]" use="Number" />

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

<xsl:template match="Modifier[key('matching-parent', ../Number)]/text()">
    <xsl:value-of select="1"/>
</xsl:template>

</xsl:stylesheet>

Applied to the following test input:

<Parent1>
    <Parent2>
        <Number>111</Number>
        <Name>Starter</Name>
        <Status>2</Status>
        <Modifier>0</Modifier>
    </Parent2>
    <Parent2>
        <Number>111</Number>
        <Name>SanJose</Name>
        <Status>1</Status>
        <Modifier>0</Modifier>
    </Parent2>
    <Parent2>
        <Number>222</Number>
        <Name>SanJose</Name>
        <Status>2</Status>
        <Modifier>0</Modifier>
    </Parent2>
    <Parent2>
        <Number>111</Number>
        <Name>Another 1</Name>
        <Status>2</Status>
        <Modifier>0</Modifier>
    </Parent2>
    <Parent2>
        <Number>222</Number>
        <Name>Another 2</Name>
        <Status>1</Status>
        <Modifier>0</Modifier>
    </Parent2>
    <Parent2>
        <Number>111</Number>
        <Name>SanJose</Name>
        <Status>2</Status>
        <Modifier>0</Modifier>
    </Parent2>
</Parent1>

produces this result:

<?xml version="1.0" encoding="UTF-8"?>
<Parent1>
   <Parent2>
      <Number>111</Number>
      <Name>Starter</Name>
      <Status>2</Status>
      <Modifier>1</Modifier>
   </Parent2>
   <Parent2>
      <Number>111</Number>
      <Name>SanJose</Name>
      <Status>1</Status>
      <Modifier>1</Modifier>
   </Parent2>
   <Parent2>
      <Number>222</Number>
      <Name>SanJose</Name>
      <Status>2</Status>
      <Modifier>0</Modifier>
   </Parent2>
   <Parent2>
      <Number>111</Number>
      <Name>Another 1</Name>
      <Status>2</Status>
      <Modifier>1</Modifier>
   </Parent2>
   <Parent2>
      <Number>222</Number>
      <Name>Another 2</Name>
      <Status>1</Status>
      <Modifier>0</Modifier>
   </Parent2>
   <Parent2>
      <Number>111</Number>
      <Name>SanJose</Name>
      <Status>2</Status>
      <Modifier>1</Modifier>
   </Parent2>
</Parent1>

Note especially the last node in the example.


Edit:

If I understand your new requirement correctly, you need to change this:

<xsl:template match="Modifier[key('matching-parent', ../Number)]/text()">
    <xsl:value-of select="1"/>
</xsl:template>

to:

<xsl:template match="Modifier[key('matching-parent', ../Number)]">
    <xsl:copy>
        <xsl:value-of select="concat('STATUS', count(../preceding-sibling::Parent2[key('matching-parent', Number)]) + 1)"/>
    </xsl:copy>
</xsl:template>

Upvotes: 1

Joel M. Lamsen
Joel M. Lamsen

Reputation: 7173

I came up with the following stylesheet from what I can understand with your question:

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

    <xsl:strip-space elements="*"/>

    <xsl:output indent="yes" omit-xml-declaration="yes"/>

    <xsl:key name="SJ" match="Parent2[contains(Name, 'SanJose') and Status = 1]" use="Number"/>

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

    <xsl:template match="Modifier">
        <xsl:copy>
            <xsl:choose>
                <xsl:when test="contains(preceding-sibling::Name, 'SanJose') 
                                and
                                preceding-sibling::Status = 1">
                    <xsl:text>1</xsl:text>
                </xsl:when>
                <xsl:when test="key('SJ', preceding-sibling::Number)">
                    <xsl:text>1</xsl:text>
                </xsl:when>
                <xsl:otherwise><xsl:value-of select="."/></xsl:otherwise>
            </xsl:choose>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

it produces:

<Parent1>
   <Parent2>
      <Number>"100"</Number>
      <Name>"SanJose"</Name>
      <Type>"SanJoseExtra"</Type>
      <Adress>Avenue 54</Adress>
      <Status>2</Status>
      <TR5>1</TR5>
      <TR10>0</TR10>
      <Modifier>1</Modifier>
   </Parent2>
   <Parent2>
      <Number>"100"</Number>
      <Name>"SanTropez"</Name>
      <Type>"SanTropezSmall"</Type>
      <Adress>British Cal 3</Adress>
      <Status>2</Status>
      <TR5>1</TR5>
      <TR10>1</TR10>
      <Modifier>1</Modifier>
   </Parent2>
   <Parent2>
      <Number>"101"</Number>
      <Name>"SanJose"</Name>
      <Type>"SanDiegoMedium"</Type>
      <Adress>French Revolution n.n.</Adress>
      <Status>2</Status>
      <TR5>1</TR5>
      <TR10>1</TR10>
      <Modifier>0</Modifier>
   </Parent2>
   <Parent2>
      <Number>"100"</Number>
      <Name>"SanJose"</Name>
      <Type>"SanJoseSmall"</Type>
      <Adress>Avenue 54</Adress>
      <Status>1</Status>
      <TR5>1</TR5>
      <TR10>0</TR10>
      <Modifier>1</Modifier>
   </Parent2>
</Parent1>

Upvotes: 1

Related Questions