Rory Perry
Rory Perry

Reputation: 97

Accessing multiple XML files via XSL

I am currently trying to access the data from multiple XML files. I've easily accessed data from the first known as Rainfall.xml but have been unable to retrieve any data from the next file in my list Max_temp.xml.

The overall objective is to combine 4-5 XML files together to include all of the data about various weather events and also the station which these events were recorded at.

The example code is below:

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

    <!-- TODO customize transformation rules 
         syntax recommendation http://www.w3.org/TR/xslt 
    -->
<xsl:variable name="maxTemp" select="document('Max_temp.xml')" />  

<xsl:template match="rainfall">
&lt;weather&gt;
      <xsl:apply-templates select="measurement" />
&lt;/weather&gt;

</xsl:template>


<xsl:template match="measurement">
    &lt;measurement&gt;
        &lt;StationNum&gt;<xsl:value-of select="StationNum"/>&lt;/StationNum&gt;
        &lt;Date&gt;<xsl:value-of select="concat(Day,'/',Month,'/',Year)"/>&lt;/Date&gt;
        <xsl:variable name="Date" select="concat(Day,'/',Month'/',Year)"/>
        &lt;Rainfall&gt;<xsl:value-of select="Volume"/>&lt;/Rainfall&gt;
        &lt;MaxTemp&gt;<xsl:value-of select="$MaxTemp/maxTemp/measurement[concat(Day,'/',Month'/',Year)].equals(Date)"/>&lt;/MaxTemp&gt;
    &lt;/measurement&gt;
</xsl:template>

</xsl:stylesheet>

The structure of the XML files being used is as follows:

<typeOfFile(Rainfall, Temp, Solar Radiation etc)>
    <measurment>
        <Code>...</Code>
        <Station>...</Station>
        <Day>...</Day>
        <Month>...</Month>
        <Year>...</Year>
        <Volume>...</Volume>
    </measurement>
</typeOfFile>

I am currently getting a non-response from the browser when trying to load the corresponding Rainfall.xml file which this XSL sheet styles.

Can someone please point me in the right direction? Also if anyone can refer me to some information about using an XSL sheet to create and format an XML file it'd be much appreciated.

Upvotes: 3

Views: 4197

Answers (1)

Tomalak
Tomalak

Reputation: 338108

The following approach would work (XSLT 1.0):

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

    <!-- select the <measurement> elements of all the various input files -->
    <xsl:variable name="maxTemp" select="document('Max_temp.xml')/*" />
    <xsl:variable name="rainfall" select="document('rainfall.xml')/*" />
    <xsl:variable name="solarRadiation" select="document('solar_radiation.xml')/*" />

    <!-- index <measurement> elements by their station and date -->
    <xsl:key name="kMeasurement" match="measurement"
        use="concat(Station, '/', Day, '/', Month, '/', Year)"
    />

    <xsl:template match="/">
        <weather>
            <xsl:apply-templates select="$maxTemp/measurement" />
        </weather>
    </xsl:template>

    <xsl:template match="measurement">
        <xsl:variable name="currentKey" select="concat(Station, '/', Day, '/', Month, '/', Year)" />

        <measurement>
            <StationNum><xsl:value-of select="Station"/></StationNum>
            <Date><xsl:value-of select="concat(Day, '/', Month, '/', Year)"/></Date>

            <!-- since we are handling maxTemp measurements here, we can output that directly -->
            <MaxTemp><xsl:value-of select="Value"/></MaxTemp>

            <!-- to access the others we need a context switch and a key lookup -->
            <xsl:for-each select="$rainfall">
                <Rainfall><xsl:value-of select="key('kMeasurement', $currentKey)/Volume"/></Rainfall>
            </xsl:for-each>
            <xsl:for-each select="$solarRadiation">
                <SolarRadiation><xsl:value-of select="key('kMeasurement', $currentKey)/Watt"/></SolarRadiation>
            </xsl:for-each>
            <!-- and so on -->
        </measurement>
    </xsl:template>
</xsl:stylesheet>

You would apply it against an empty dummy input document (something like a simple <xml />).

It then loads all the actual documents into variables and follows the entries in one of them to create the output. I chose the max temp measurements, but if all files contain data points for the same dates it won't matter which one.

Each of those max temp data points generates one output <measurement> element.

For this it uses an <xsl:key> to pull the correct measurement out of the related documents. The <xsl:key> is a key/value store (i.e. dictionary, hashtable): it indexes the nodes by a certain key string, in our case the combination of station ID and date.

But it returns only those nodes that are in the same document as the current node. Therefore, to output anything outside of Max_temp.xml, we must switch the context to another document. The <xsl:for-each> does that, so we use it here to set the scope for our call to key() ($rainfall and $solarRadiation only hold a single element anyway).

Note that since I had to guess at your actual input document structure, a few XPaths might be off. Adapt them accordingly.

Upvotes: 4

Related Questions