Pankaj Jaju
Pankaj Jaju

Reputation: 5471

Update node value based on other node's value

I am trying to update one node's attribute based on other node's value.

My XML:

<Report>
  <Action rID="T4">
    <Step rID="T5">
      <Obj ><![CDATA[BAR]]></Obj>
      <Details ><![CDATA[Total files to compare = 1]]></Details>
      <Time><![CDATA[05/06/2015 - 20:41:07]]></Time>
      <TimeTick>1433533267</TimeTick>
      <NodeArgs eType="User" icon="5" nRep="8" Source="Action1" status="Done"  SourceLine="-1" >
    <Disp><![CDATA[BAR]]></Disp>
      </NodeArgs>
    </Step>
    <Step rID="T7">
      <Obj ><![CDATA[File 1 : Passed]]></Obj>
      <Details ><![CDATA[Baseline = C:\Baseline\BAR\1759982021.xml
      Outbound = C:\Outbound\BAR\1759982021.xml]]></Details>
      <Time><![CDATA[05/06/2015 - 20:41:07]]></Time>
      <TimeTick>1433533267</TimeTick>
      <NodeArgs eType="User" icon="5" nRep="10" status="Passed" Source="Action1" SourceLine="-1" >
    <Disp><![CDATA[File 1 : Passed]]></Disp>
      </NodeArgs>
    </Step>
    <Step rID="T9">
      <Obj ><![CDATA[PASS]]></Obj>
      <Details ><![CDATA[]]></Details>
      <Time><![CDATA[05/06/2015 - 20:41:08]]></Time>
      <TimeTick>1433533268</TimeTick>
      <NodeArgs eType="User" icon="5" nRep="12" status="Information" Source="Action1" SourceLine="-1" >
    <Disp><![CDATA[PASS]]></Disp>
      </NodeArgs>
    </Step>

    <Step rID="T71">
      <Obj><![CDATA[MSP]]></Obj>
      <Details><![CDATA[Total files to compare = 1]]></Details>
      <Time><![CDATA[06/06/2015 - 20:58:02]]></Time>
      <TimeTick>1433620682</TimeTick>
      <NodeArgs eType="User" icon="5" nRep="74" Source="Action1"  SourceLine="-1">
        <Disp><![CDATA[MSP]]></Disp>
      </NodeArgs>
    </Step>
    <Step rID="T72">
      <Obj><![CDATA[File 1 : Passed]]></Obj>
      <Details><![CDATA[Baseline = C:\Baseline\MSP\G82164M1225983TN000073914GEU9.xml
    Outbound = C:\Outbound\MSP\G82164M1225983TN000073914GEU9.xml]]>
    </Details>
      <Time><![CDATA[06/06/2015 - 20:58:02]]></Time>
      <TimeTick>1433620682</TimeTick>
      <NodeArgs eType="User" icon="5" nRep="75" status="Passed"  Source="Action1" SourceLine="-1">
        <Disp><![CDATA[File 1 : Failed]]></Disp>
      </NodeArgs>
    </Step>
    <Step rID="T73"><Obj> <![CDATA[FAIL]]></Obj>
      <Details><![CDATA[]]></Details>
      <Time><![CDATA[06/06/2015 - 20:58:02]]></Time>
      <TimeTick>1433620682</TimeTick>
      <NodeArgs eType="User" icon="5" nRep="76" status="Information"  Source="Action1" SourceLine="-1">
        <Disp><![CDATA[FAIL]]></Disp>
      </NodeArgs>
    </Step>
  </Action>
</Report>

My output would be HTML. In my XML, there may be numerous blocks of Steps. For each block of steps, there would only be one Done and Information steps. So for each block of Done steps, the Information step informs if the block is PASS/FAIL. Hence i wanted to transform the XML to XML and then to HTML without hardcoding the CDATA.

Even if I can change Status = Done to Status = PASSDone, that would also be useful.

My XSL so far

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="utf-8" method="html" omit-xml-declaration="yes" indent="yes"/>

<xsl:template match="/Report/Action">
    <html>
      <head>
        <style type="text/css">
          body { font-family:Tahoma; font-size:9pt; }
          h2 {color: #b48608; font-style: italic; text-align: center; text-decoration: underline;}
          table { table-layout: auto; }
          table, th, td { font-family:Tahoma; font-size:9pt; padding:5px;  border-collapse:collapse; vertical-align:top; border:1px solid black; white-space:nowrap; }
          th, tr.Venue { text-align:left; background-color:#D3D3D3; font-weight: bold; }
          td.Passed { font-size:11pt; color:Green; text-align:center; }
          td.Failed { font-size:11pt; color:Red; text-align:center; }
          tr.Passed { background-color:#AAEEAA; font-weight:bold; }
          tr.Failed { background-color:#FFAAAA; font-weight:bold; }
        </style>
      </head>
      <body>
        <table>
          <th>Venues</th>
          <th>Status</th>
          <xsl:variable name="VenueTestStatus" select="Step/NodeArgs[@status='Information']/Disp"/>
          <xsl:variable name="VenueName" select="Step/NodeArgs[@status='Done']/Disp"/>
          <xsl:for-each select="$VenueTestStatus">
            <xsl:variable name="i" select="position()"/>
            <tr>
              <xsl:if test="$VenueTestStatus[$i]='PASS'">
                <xsl:attribute name="class">Passed</xsl:attribute>
              </xsl:if>
              <xsl:if test="$VenueTestStatus[$i]='FAIL'">
                <xsl:attribute name="class">Failed</xsl:attribute>
              </xsl:if>              
              <td>
                <a>
                  <xsl:attribute name="href">
                    <xsl:value-of select="concat('#',$VenueName[$i])" />
                  </xsl:attribute>
                  <xsl:value-of select="$VenueName[$i]" />
                </a>
              </td>
              <td>
                <xsl:value-of select="$VenueTestStatus[$i]" />
              </td>
            </tr>
          </xsl:for-each>
        </table>

        <br/>
        <hr/>
        <br/>

        <table>
            <xsl:for-each select="Step">
              <xsl:if test="NodeArgs/@status != 'Information'">
                <tr>
                  <xsl:variable name="IsVenueRow">
                    <xsl:value-of select="NodeArgs/Disp" disable-output-escaping="no"/>
                  </xsl:variable>
                  <xsl:if test="not(starts-with($IsVenueRow, 'File'))">
                    <xsl:attribute name="class">Venue</xsl:attribute>
                  </xsl:if>

                  <td>
                    <xsl:variable name="StatusSymbol">
                      <xsl:value-of select="NodeArgs/@status" disable-output-escaping="no"/>
                    </xsl:variable>
                    <xsl:attribute name="class">
                      <xsl:value-of select="$StatusSymbol" />
                    </xsl:attribute>
                    <xsl:choose>
                      <xsl:when test="NodeArgs/@status = 'Passed'">
                        <xsl:text>&#10004;</xsl:text>
                      </xsl:when>
                      <xsl:when test="NodeArgs/@status = 'Failed'">
                        <xsl:text>&#10008;</xsl:text>
                      </xsl:when>
                    </xsl:choose>
                  </td>

                  <td>
                    <xsl:choose>
                      <xsl:when test="not(starts-with($IsVenueRow, 'File'))">
                        <a>
                          <xsl:attribute name="name">
                            <xsl:value-of select="$IsVenueRow" />
                          </xsl:attribute>
                          <xsl:value-of select="$IsVenueRow" />
                        </a>
                      </xsl:when>
                      <xsl:otherwise>
                        <xsl:value-of select="$IsVenueRow" />
                      </xsl:otherwise>
                    </xsl:choose>
                   </td>
                </tr>
            </xsl:if>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>

</xsl:stylesheet>

Upvotes: 0

Views: 150

Answers (2)

har07
har07

Reputation: 89325

One possible XSL transformation :

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

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

    <xsl:template match="Action[Step/NodeArgs[@status='Information']/Disp = 'PASS']/Step/NodeArgs[@status='Done']/Disp">
        <xsl:copy>
           <xsl:text><![CDATA[PASSBAR]]></xsl:text>
         </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

Brief explanation :

  1. Identity template (<xsl:template match="node()|@*">) copy every node as it is in the source XML
  2. The other template overrides the identity template rule. It is selecting Step/NodeArgs[@status='Done']/Disp elements where corresponding Step/NodeArgs[@status='Information']/Disp element value equals "PASS", and replace the selected Disp element value with <![CDATA[PASSBAR]]> in the output XML.

Upvotes: 1

Zach Young
Zach Young

Reputation: 11223

I think you want an identity transform, then build <Disp/> based on your criteria:

<xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>
  <xsl:template match="node()|@*" >
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Disp">
    <Disp>
      <xsl:choose>

    <xsl:when test="../../NodeArgs[@status='Information']">
      PASS
    </xsl:when>

    <xsl:when test="../../NodeArgs[@status='Done']">
      PASSBAR
    </xsl:when>

    <xsl:otherwise>
      <!-- Copy this (Disp) tree as is -->
      <xsl:copy>
        <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>
    </xsl:otherwise>

      </xsl:choose>
    </Disp>
  </xsl:template>
</xsl:stylesheet>

This is the identity transform part:

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

by itself, it would just copy the source document as it was.

By matching on <Disp/>:

<xsl:template match="Disp">

we can stop the processor from just copying <Disp/> and interject our own version of it:

<Disp>
  <xsl:choose>
    <xsl:when test="...">

and if nothing matches our <xsl:when/> conditions, we start the identity transform again:

<xsl:otherwise>
  <!-- Copy this (Disp) tree as is -->
  <xsl:copy>
    <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
</xsl:otherwise>

to continue with the node/attribute-for-node/attribute copy.

Upvotes: 0

Related Questions