Simple problem: I have an XML file like this:
<Locations>
<Location>/Simulation/@ID</Location>
<Location>/Simulation/Loans</Location>开发者_C百科
<Location>/Simulation/Assets</Location>
<Location>/Simulation/BankAssets</Location>
<Location>/Simulation/RealEstates</Location>
</Locations>
I also have a second XML file containing data which matches these XPath nodes. And I need to create an XSLT 1.0 without any scripting that can transform this data file to just generate a list of data that matches these nodes. Something like this:
<Data>
<Item Node="/Simulation/@ID">
<Value>1</Value>
<Value>2</Value>
<Value>3</Value>
</Item>
<Item Node="/Simulation/Loans">
<Value>1024</Value>
<Value>555</Value>
<Value>0</Value>
</Item>
</Data>
The number of Value nodes per item is unimportant. Nor is the relation between the values of different items important. Basically, the stylesheet is just meant to collect simple, statistical data to be summed up, averaged and whatever more. The list of locations van vary a lot, depending on what the user wants. This example is just some spoof data.
Question: how to collect this information?
There are actually two ways of doing this - one is to perform two-phase transformation and another one is to use evaluate
extension function.
Two-phase transformation
First you need to generate correct XSL based on your Locations
list. It could look like this:
<xsl:template match="/">
<Data>
<Item Node="/Simulation/@ID">
<xsl:apply-templates select="/Simulation/@ID"/>
</Item>
<Item Node="/Simulation/Loans">
<xsl:apply-templates select="/Simulation/Loans"/>
</Item>
<!-- ... and so on ... -->
</Data>
</xsl:template>
<xsl:template match="node()|@*">
<Value>
<xsl:value-of select="."/>
</Value>
</xsl:template>
I assume you don't have much troubles with creating transformation that will produce this output based on your XPath definition file as pattern is pretty simple.
The next step is to apply generated transformation to your source file. You could combine these two steps into small pipe and get results you want.
Using extension function
Library EXSLT contains extension function called evaluate which helps in cases like yours. It's supported by Xalan transformer out of the box but not by Saxon as far as I know. There is however major issue with this function - starting with version 2.7 of Xalan there is a bug which prevents execution of multiple evaluations. Unfortunately stylesheet below is affected when I tried to run it. Suggestion would be to change version of Xalan to 2.6 if possible. Nevertheless, here's the stylesheet that will do what you want without additional generation phase.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dyn="http://exslt.org/dynamic" extension-element-prefixes="dyn">
<xsl:variable name="locations" select="document('xpath.xml')/Locations"/>
<xsl:variable name="root" select="/"/>
<xsl:template match="/">
<Data>
<xsl:for-each select="$locations/Location">
<Item Node="{.}">
<xsl:variable name="currentLocation" select="concat('$root', .)"/>
<xsl:apply-templates select="dyn:evaluate($currentLocation)"/>
</Item>
</xsl:for-each>
</Data>
</xsl:template>
<xsl:template match="node()|@*">
<Value>
<xsl:value-of select="."/>
</Value>
</xsl:template>
</xsl:stylesheet>
Assumption is that the document with Locations definitions is on a same directory and is called xpath.xml
. Update document()
function usage if it's not the case.
精彩评论