Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to process all data from multiple XML files after merging using XSL

Tags:

xml

xslt

I am using XSL to read in three XML documents which are identical except their attributes have different values (potentially I will be reading many XML files). I want to count the number of times the "outcome" attribute has a value "Passed" or "Failed" for the element whose "testName" attribute = "TestOne". I am achieving this using the following:

File1.xml

<container>
    <build>
        <Tests>
            <Results>
                <Result testName="TestOne" outcome="Passed" ></Result>
                <Result testName="TestTwo"  outcome="Passed" ></Result>
            </Results>
        </Tests>    
    </build>
</container>

File2.xml

<container>
    <build>
        <Tests>
            <Results>
                <Result testName="TestOne" outcome="Passed" ></Result>
                <Result testName="TestTwo"  outcome="Failed" ></Result>
            </Results>
        </Tests>    
    </build>
</container>

File3.xml

<container>
    <build>
        <Tests>
            <Results>
                <Result testName="TestOne" outcome="Failed" ></Result>
                <Result testName="TestTwo"  outcome="Failed" ></Result>
            </Results>
        </Tests>    
    </build>
</container>

Index.xml

<?xml-stylesheet type="text/xsl" href="merge3.xsl"?>
<list>
    <entry name="File1.xml" />
    <entry name="File2.xml" />
    <entry name="File3.xml" />
</list>

Merge2.xsl

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

<xsl:template match="/">
    <xsl:for-each select="/list/entry">
        <xsl:apply-templates select="document(@name)/container/build/*[local-name()='Tests']" />
    </xsl:for-each>
</xsl:template>

<xsl:template match="*[local-name()='Results']">
    <xsl:variable name="name" select="'TestOne'" />
    <xsl:variable name="totalPassed" select="*[local-name()='Result'][@testName = 'TestOne'][@outcome = 'Passed']" />
    <xsl:variable name="totalFailed" select="*[local-name()='Result'][@testName = 'TestOne'][@outcome = 'Failed']" />

    <h2>Totals</h2>
    <table border="1" cellSpacing="0" cellPadding="5" >
        <tr bgcolor="#9acd32">
            <th>Test Name</th>
            <th>Total Passed</th>
            <th>Total Failed</th>
        </tr>
        <tr>
            <td><xsl:value-of select="$name"/></td>
            <td><xsl:value-of select="count($totalPassed)"/></td>
            <td><xsl:value-of select="count($totalFailed)"/></td>
        </tr>
    </table>
</xsl:template>

</xsl:stylesheet>

The result produces 3 Totals tables. My intention is to display one Totals table displaying how many times TestOne has passed and failed in all the XML documents. It seems each XML document is being read/selected and then processed one at a time. I'd like to read in and select all the XML files before processing them.

like image 569
user2734805 Avatar asked Aug 31 '13 15:08

user2734805


1 Answers

You could gather all the Result elements from across the three documents using something like

<xsl:variable name="allResults"
  select="(/ | document('file2.xml') | document('file3.xml'))//Result" />

and then apply predicates to this to count the elements you're interested in, for example

<xsl:value-of select="
  count($allResults[@testName = 'TestOne'][@outcome = 'Failed'])" />

Instead of a fixed set of file names, if you have a main index.xml that lists all the files you want to combine, for example:

<list>
    <entry name="File1.xml" />
    <entry name="File2.xml" />
    <entry name="File3.xml" />
</list>

then you can use this index as the main input to your stylesheet and the allResults variable becomes:

<xsl:variable name="allResults"
  select="document(/list/entry/@name)//Result" />

When you pass a node set to the document function it takes the string value of each node in turn and treats that as the URI of a file to load, returning the resulting set of document root nodes.

Here's a complete example

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

  <xsl:variable name="allResults"
    select="document(/list/entry/@name)//Result" />

  <xsl:template match="/">
    <xsl:variable name="name" select="'TestOne'" />

    <h2>Totals</h2>
    <table border="1" cellSpacing="0" cellPadding="5" >
        <tr bgcolor="#9acd32">
            <th>Test Name</th>
            <th>Total Passed</th>
            <th>Total Failed</th>
        </tr>
        <tr>
            <td><xsl:value-of select="$name"/></td>
            <td><xsl:value-of select="count($allResults[@testName = $name]
                                              [@outcome = 'Passed'])"/></td>
            <td><xsl:value-of select="count($allResults[@testName = $name]
                                              [@outcome = 'Failed'])"/></td>
        </tr>
    </table>
  </xsl:template>

</xsl:stylesheet>
like image 195
Ian Roberts Avatar answered Sep 28 '22 10:09

Ian Roberts