Reputation: 1167
Its me again, but this time i have a real problem... I have one XML, and i have to transform it in another XML by using a filter of another XML
File_in.xml:
<?xml version="1.0" encoding="ISO-8859-15"?>
<root>
<item>
<server>001023541</server>
<name>P1</name>
<desc>Production</desc>
<status>1</status>
<ram>1024</ram>
<hdd>8 To</hdd>
</item>
<item>
<server>201012345</server>
<name>P2</name>
<desc>Production</desc>
<status>4</status>
<ram>2048</ram>
<hdd>8 To</hdd>
</item>
<item>
<server>120332416</server>
<name>P1</name>
<desc>Production</desc>
<status>2</status>
<ram>8196</ram>
<hdd>8 To</hdd>
</item>
</root>
And another XML:
filter.xml
<?xml version="1.0" encoding="ISO-8859-15"?>
<Filtre>
<Bloc5>
<Part1>
<EAN>001023541</EAN>
<EAN>012356549</EAN>
<EAN>012356559</EAN>
<EAN>012356569</EAN>
</Part1>
<Part2>
<EAN>201012345</EAN>
<EAN>201012346</EAN>
<EAN>201012347</EAN>
</Part2>
</Bloc5>
</Filtre>
If /root/item/server matches with an element of /Filtre/Bloc5/Part1/EAN, i replace
<item>
<server>001023541</server>
<name>P1</name>
<desc>Production</desc>
<status>1</status>
<ram>1024</ram>
<hdd>8 To</hdd>
</item>
by
<item>
<server>MAIN</server>
<status>PRODUCTION</status>
</item>
otherwise if /root/item/server matches with an element of /Filtre/Bloc5/Part2/EAN, i replace
<item>
<server>201012345</server>
<name>P2</name>
<desc>Production</desc>
<status>4</status>
<ram>2048</ram>
<hdd>8 To</hdd>
</item>
by
<item>
<server>BACKUP</server>
<status>STOPPED</status>
</item>
and the other are automatically replaced like:
<item>
<server>120332416</server>
<name>P1</name>
<desc>Production</desc>
<status>2</status>
<ram>8196</ram>
<hdd>8 To</hdd>
</item>
by
<item>
<name>OFFLINE</name>
<desc>Production</desc>
</item>
I get the name of the XML (used to filter) by this way:
<!-- filter settings -->
<xsl:param name="filter_xml" />
<xsl:variable name="filter" select="document('$filter_xml')" />
And there is my code :
<xsl:for-each select="root/item">
<xsl:choose>
<xsl:when test="index-of(($filter/Filtre/Bloc5/Part1/EAN), ./server)">
<item>
<server>MAIN</server>
<status>PRODUCTION</status>
</item>
</xsl:when>
<xsl:when test="index-of(($filter/Filtre/Bloc5/Part2/EAN), ./server)" >
<item>
<server>BACKUP</server>
<status>STOPPED</status>
</item>
</xsl:when >
<xsl:otherwise>
<item>
<name>OFFLINE</name>
<desc>Production</desc>
</item>
</xsl:otherwise>
</xsl:for-each>
But this does not work...
Upvotes: 1
Views: 1390
Reputation: 12729
A much cleaner, simpler and efficient solution is to use template matching instead of tortuous branching. I am assuming XSLT 2.0 because you did not tag your question with the XSLT version number, but if you need XSLT 1.0, then adjustments can easily be made.
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*" />
<xsl:variable name="filter" select="document('filter.xml')/*" />
<xsl:template match="/" >
<root>
<xsl:apply-templates select="*/item" />
</root>
</xsl:template>
<xsl:template match="item[server=$filter/Bloc5/Part1/EAN]" >
<item>
<server>MAIN</server>
<status>PRODUCTION</status>
</item>
</xsl:template>
<xsl:template match="item[server=$filter/Bloc5/Part2/EAN]" >
<item>
<server>BACKUP</server>
<status>STOPPED</status>
</item>
</xsl:template>
<xsl:template match="item" >
<item>
<name>OFFLINE</name>
<desc>Production</desc>
</item>
</xsl:template>
</xsl:stylesheet>
Thanks for your acceptance. As icing on the cake, you could also replace the first two item templates with this ONE more generic and extensible template. But it's debatable whether it is more simpler or more complex.
Alternative generic item template (only works for XSLT 2.0) ...
<xsl:template match="item[server=$filter/Bloc5/*/EAN]" >
<xsl:variable name="part-no"
select="substring-after(local-name(($filter/Bloc5/*[EAN=current()/server])[1]),
'Part') cast as xs:integer" />
<xsl:copy>
<server><xsl:value-of select="('MAIN' ,'BACKUP' )[$part-no]" /></server>
<status><xsl:value-of select="('PRODUCTION','STOPPED')[$part-no]" /></status>
</xsl:copy>
</xsl:template>
Upvotes: 2
Reputation: 66783
Two things that you need to adjust:
The path for the document()
needs to be the value of the filter variable. You are currently passing the string "$filter". You need to change
<xsl:variable name="filter" select="document('$filter_xml')" />
to <xsl:variable name="filter" select="document($filter_xml)" />
The contains()
function expects string inputs for each of the parameters. In order to evaluate the contains()
test against each of the filter's EAN
, perform the test inside of a predicate filter on the EAN
elements. For example, change: <xsl:when test="index-of(($filter/Filtre/Bloc5/Part1/EAN), ./server)">
to <xsl:when test="$filter/Filtre/Bloc5/Part1/EAN[contains(., ./server)]">
. If anything matches, the result will be at least one EAC
, which will evaluate to true()
in the test.
Applied to an example XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<xsl:param name="filter_xml" />
<xsl:variable name="filter" select="document($filter_xml)" />
<xsl:template match="/">
<xsl:for-each select="root/item">
<xsl:choose>
<xsl:when test="$filter/Filtre/Bloc5/Part1/EAN[contains(., ./server)]">
<item>
<server>MAIN</server>
<status>PRODUCTION</status>
</item>
</xsl:when>
<xsl:when test="$filter/Filtre/Bloc5/Part2/EAN[contains(., ./server)]" >
<item>
<server>BACKUP</server>
<status>STOPPED</status>
</item>
</xsl:when >
<xsl:otherwise>
<item>
<name>OFFLINE</name>
<desc>Production</desc>
</item>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Upvotes: 1