XSLT Template is never called even so the match exists - xslt-1.0

I want to know why the template match="*" mode="Value_Or_Null_with_comma" is not called and executed?
The following includes my XSL code:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:abc="urn:example-x-
com:abc" xmlns:ns3="http://Example1.com" xmlns:ns4="http://Example2.com" exclude-result-
prefixes="abc" version="1.0">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:output method="text" />
<xsl:template match="ns3:productionOrder">
<xsl:apply-templates select="ns4:ProductionOrder/ns4:productionOrderData/ns4:ProductionOrderData"
mode="Technical"></xsl:apply-templates>
</xsl:template>
<xsl:template match="node()">
<xsl:if test="/ns3:AddProductionOrder">
<xsl:apply-templates select="ns3:productionOrder"></xsl:apply-templates>
</xsl:if>
</xsl:template>
<xsl:template match="*" mode="Value_Or_Null_with_comma">
<xsl:choose>
<xsl:when test=". and ./text()">
<!--test="$Parameter and $Parameter/text()"-->
<xsl:choose>
<xsl:when test="number(.) = .">
<xsl:value-of select="."></xsl:value-of>
</xsl:when>
<xsl:otherwise>'<xsl:value-of select="."></xsl:value-of>'</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>NULL</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="ns4:ProductionOrderData" mode="Technical">
<xsl:text>Exec RecipeData_List,
</xsl:text>
<apply-templates select="ns4:materialNumber" mode="Value_Or_Null_with_comma"></apply-templates>
<!--,-->
<xsl:text></xsl:text>NULL x1,
<xsl:text></xsl:text>NULL x2,
</xsl:template>
</xsl:stylesheet>
The according XML file is:
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="Test MODE_method call.xsl"?>
<ns3:AddProductionOrder xmlns:ns3="http://Example1.com">
<ns3:productionOrder>
<ns4:ProductionOrder xmlns:ns4="http://Example2.com">
<ns4:productionOrderData>
<ns4:ProductionOrderData>
<ns4:Comment1>Mele1</ns4:Comment1>
<ns4:materialNumber>10001895</ns4:materialNumber>
<ns4:maxFimeAllowed>0.0 </ns4:maxFimeAllowed>
</ns4:ProductionOrderData>
</ns4:productionOrderData>
</ns4:ProductionOrder>
</ns3:productionOrder>
</ns3:AddProductionOrder>
The result is (but it's wrong, it does not display the contents of the material number):
Exec RecipeData_List,
NULL x1,
NULL x2,
The result should be:
Exec RecipeData_List,
10001895,
NULL x1,
NULL x2,
One more hint:
I'm using here mode "Technical" because later I would transform more data from ns4:ProductionOrderData, but this is not content of the posted code, it's just a shorter version with the same problem that Value_Or_Null_with_comma is not executed.
Thank you for your help!

Related

XSLT 1.0: How to determine the position of a node based on the value

thanks for taking the time!
I am trying to get the position of a node based on its value, so that I can then get corresponding entries. Please let me show you with an example, it will be easier to explain.
Here is the source XML:
<?xml version="1.0" encoding="UTF-8"?>
<sampleSourceXML>
<Table>
<Header>
<Column>Name</Column>
<Column>Surname</Column>
<Column>Title</Column>
</Header>
<Body>
<Row>
<Entry>James</Entry>
<Entry>Bond</Entry>
<Entry>Mr</Entry>
</Row>
<Row>
<Entry>Harry</Entry>
<Entry>Potter</Entry>
<Entry>Mr</Entry>
</Row>
</Body>
</Table>
</sampleSourceXML>
This is the resulting XML that I would like to achieve:
<?xml version="1.0" encoding="UTF-8"?>
<sampleResultXML>
<Person>
<FirstName>James</FirstName>
<LastName>Bond</LastName>
<Title>Mr</Title>
</Person>
<Person>
<FirstName>Harry</FirstName>
<LastName>Potter</LastName>
<Title>Mr</Title>
</Person>
</sampleResultXML>
I tried it with this XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:element name="sampleResultXML">
<xsl:for-each select="sampleSourceXML/Table/Rows/Row">
<xsl:element name="Person">
<xsl:element name="FirstName">
<xsl:value-of select="./Entry[1]/text()"></xsl:value-of>
</xsl:element>
<xsl:element name="LastName">
<xsl:value-of select="./Entry[2]/text()"></xsl:value-of>
</xsl:element>
<xsl:element name="Title">
<xsl:value-of select="./Entry[3]/text()"></xsl:value-of>
</xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
This works.
But! The source XML can have a different order. For example the Title element can be above the Name element. So the hardcoded position in my XSLT (e.g. Entry[3]) will not work any more.
The header order and the entry order are always corresponding. So if the Column Title is in position 3, so will be the corresponding row.
So I need a way to get the position of, let's say, the Title element dynamically. So I have to go through the Column elements until I reach the one with value "Title". This position I thought I'd save in a variable (e.g. Position_Title) so that I can fill the value later like so:
<xsl:element name="Title">
<xsl:value-of select="./Entry[$Position_Title]/text()"></xsl:value-of>
</xsl:element>
However I haven't been able to find a way to do this...
Do you have any ideas how I can achieve this?
Thanks!
Nick
EDIT: Thanks to Michael for the help! Here is the final XSLT I'm using:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/sampleSourceXML">
<xsl:variable name="cols" select="Table/Header/Column"/>
<sampleResultXML>
<xsl:for-each select="Table/Body/Row">
<Person>
<xsl:for-each select="Entry">
<xsl:variable name="i" select="position()"/>
<xsl:variable name="elementName">
<xsl:choose>
<xsl:when test="$cols[$i]='Name'">
<xsl:value-of select="'FirstName'"/>
</xsl:when>
<xsl:when test="$cols[$i]='Surname'">
<xsl:value-of select="'LastName'"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$cols[$i]"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:element name="{$elementName}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</Person>
</xsl:for-each>
</sampleResultXML>
</xsl:template>
</xsl:stylesheet>
So I need a way to get the position of, let's say, the Title element dynamically. So I have to go through the Column elements until I reach the one with value "Title". This position I thought I'd save in a variable (e.g. Position_Title) so that I can fill the value later like so:
I think it could be much simpler if you do it from the opposite direction. Try:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/sampleSourceXML">
<xsl:variable name="cols" select="Table/Header/Column" />
<sampleResultXML>
<xsl:for-each select="Table/Body/Row">
<Person>
<xsl:for-each select="Entry">
<xsl:variable name="i" select="position()" />
<xsl:element name="{$cols[$i]}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</Person>
</xsl:for-each>
</sampleResultXML>
</xsl:template>
</xsl:stylesheet>

XSLT 1.0 multiple call of a template

I need to replace strings in an XML file. I'm trying to use XSLT to do it. I want to use call-template for each string I need to replace.
When I make multiple call to the template, only the last call works fine.
The XML file I need to change : I want to replace the strings
‘
and
—
by spaces
<?xml version="1.0" encoding="UTF-8"?>
<RAPPORT>
<reason>start test_145 : ‘ and test_ 151 : — _end_test</reason>
</RAPPORT>
The template I use :
<xsl:template name="globalReplace">
<xsl:param name="outputString"/>
<xsl:param name="target"/>
<xsl:param name="replacement"/>
<xsl:choose>
<xsl:when test="contains($outputString,$target)">
<xsl:value-of select="concat(substring-before($outputString,$target),$replacement)"/>
<xsl:call-template name="globalReplace">
<xsl:with-param name="outputString" select="substring-after($outputString,$target)"/>
<xsl:with-param name="target" select="$target"/>
<xsl:with-param name="replacement" select="$replacement"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$outputString"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
The multiple calls :
<xsl:template match="text()">
<xsl:call-template name="globalReplace">
<xsl:with-param name="outputString" select="."/>
<xsl:with-param name="target" select="'‘'"/>
<xsl:with-param name="replacement" select="' '"/>
</xsl:call-template>
</xsl:template>
<xsl:template match="text()">
<xsl:call-template name="globalReplace">
<xsl:with-param name="outputString" select="."/>
<xsl:with-param name="target" select="'—'"/>
<xsl:with-param name="replacement" select="' '"/>
</xsl:call-template>
</xsl:template>
Expected result :
<?xml version="1.0" encoding="UTF-8"?>
<RAPPORT>
<reason>debut test_test_145 et test 151 _fin test</reason>
</RAPPORT>
What I get in fact :
<?xml version="1.0" encoding="UTF-8"?>
<RAPPORT>
<reason>debut test_test_145**PUI** et test 151 _fin test</reason>
</RAPPORT>
PUI means an unexpected character instead of the needed space
You have two templates that match text(). Only the last of these is applied - see: https://www.w3.org/TR/1999/REC-xslt-19991116#conflict
In the given example, you could simply use the translate() function to do all the work at once, since the "strings" you want to replace are actually characters:
XML
<?xml version="1.0" encoding="UTF-8"?>
<RAPPORT>
<reason>start test_145 : ‘ and test_ 151 : — _end_test</reason>
</RAPPORT>
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="translate(., '‘—', ' ')"/>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<RAPPORT>
<reason>start test_145 : and test_ 151 : _end_test</reason>
</RAPPORT>
Many thanks !
It works fine this way !
I improved it inserting my result into a variable :
<xsl:template match="text()">
<xsl:variable name="premierePasse">
<xsl:value-of select="translate(., '‘—’',' ')"/>
</xsl:variable>
<xsl:value-of select="translate($premierePasse, 'œ', 'œ')"/>
</xsl:template>

Apply templates that matchs two conditions

I need to list all the INST names but only if the "onlyTesters" node don´t exists in the "inst/idef" part of XML body above.
I know thats strange but I can´t change the XML I receive.
XML:
<river>
<station num="699">
<inst name="FLU(m)" num="1">
<idef></idef>
</inst>
<inst name="Battery(V)" num="18">
<idef>
<onlyTesters/>
</idef>
</inst>
</station>
<INST name="PLU(mm)" num="0" hasData="1" virtual="0"/>
<INST name="FLU(m)" num="1" hasData="1" virtual="0"/>
<INST name="Q(m3/s)" num="3" hasData="1" virtual="1"/>
<INST name="Battery(V)" num="18" hasData="1" virtual="0"/>
</river>
XSL:
<xsl:template match="/">
<xsl:apply-templates select="//INST[#hasData = 1 and not(//inst[#num=(current()/#num)]/idef/onlyTesters)]/#name"/>
</xsl:template>
<xsl:template match="//INST[#hasData = 1 and not(//inst[#num=(current()/#num)]/idef/onlyTesters)]/#name">
<xsl:value-of select="#name"/>,
</xsl:template>
I´m having no match.
This is the result I expect:
PLU(mm),FLU(m),Q(m3/s)
You can achieve this with only one template:
<xsl:template match="/">
<xsl:for-each select="//INST[#hasData='1' and not(#name=//inst[idef/onlyTesters]/#name)]">
<xsl:value-of select="#name"/>
<xsl:if test="position() != last()">, </xsl:if>
</xsl:for-each>
</xsl:template>
Output is:
PLU(mm), FLU(m), Q(m3/s)
Cross-references are best resolved using a key - for example:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8" />
<xsl:key name="inst" match="inst" use="#name" />
<xsl:template match="/river">
<xsl:for-each select="INST[#hasData = 1 and not(key('inst', #name)/idef/onlyTesters)]">
<xsl:value-of select="#name"/>
<xsl:if test="position() != last()">,</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Or even simpler:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8" />
<xsl:key name="exclude" match="onlyTesters" use="ancestor::inst/#name" />
<xsl:template match="/river">
<xsl:for-each select="INST[#hasData = 1 and not(key('exclude', #name))]">
<xsl:value-of select="#name"/>
<xsl:if test="position() != last()">, </xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

xslt - create empty file using xslt 1.0

I am trying to create an empty file through xslt.
The input sample is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Businessman>
<siblings>
<sibling>John </sibling>
</siblings>
<child> Pete </child>
<child> Ken </child>
</Businessman>
When the input contains any presence of 'child' tags, it should produce the file AS IS. When the input does not have any 'child' tag, I need an empty file (0 byte file) created.
This is what I tried:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:template match="#*|node()">
<xsl:choose>
<xsl:when test="/Businessman/child">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
This gives the file unchanged when there is any 'child' tag present. But did not produce any empty file when there is no 'child' tag.
The file I need to test will look like:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Businessman>
<siblings>
<sibling>John </sibling>
</siblings>
</Businessman>
Any help would be great!
Thanks
If you want the processor to go to the trouble of opening the output file, you have to give it something to write to the output file. Try an empty text node. And you only need to make the decision 'copy or not?' once.
One way to make the decision just once and produce empty output if the condition is not met would be to replace your template with:
<xsl:template match="/">
<xsl:choose>
<xsl:when test="/Businessman/child">
<xsl:copy-of select="*"/>
</xsl:when>
<xsl:otherwise>
<xsl:text/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
This works as expected with xsltproc. (If you find yourself getting a file containing an XML declaration and nothing else, try adjusting the parameters on xsl:output.)
But when I have found myself with a similar situation (perform this transform if condition C holds, otherwise ...), I have simply added a template for the document node that would look something like this for your case:
<xsl:choose>
<xsl:when test="/Businessman/child">
<xsl:apply-templates/>
</
<xsl:otherwise>
<xsl:message terminate="yes">No children in this input, dying ...</
</
</
That way I get no output at all rather than zero-length output.
Simple enough - Just don't try to do everything in one template, don't forget to omit the xml declaration and get the xpath right:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="Businessman[child]" priority="9">
<xsl:element name="Businessman">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="Businessman" priority="0" />
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Remove all single characters from string using xslt1.0

<title>
<article_title>Land a b c d Band</article_title>
</title>
using the following function
replace(article_title, '(^[^ ]+)(.+\s+)([^ ]+)$', '$1 $3')
this string in is transformed to Land Band which is exactly what i want.
but the problem is i need this solution in xslt 1.0 since the java app that i am working with can only handle xslt 1.0 parsing.
This XSLT 1.0 transformation (there is a nasty SO bug and the code isn't indented -- I apologize for this visual mess...):
<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="text()" name="removeSingles">
<xsl:param name="pText" select="."/>
<xsl:variable name="vText" select="normalize-space($pText)"/>
<xsl:if test="string-length($vText)">
<xsl:variable name="vLeftChars" select=
"substring-before(concat($vText, ' '), ' ')"/>
<xsl:if test="string-length($vLeftChars) >1">
<xsl:value-of select="$vLeftChars"/>
<xsl:if test=
"not(string-length($vLeftChars)
>=
string-length($vText)
)
">
<xsl:text> </xsl:text>
</xsl:if>
</xsl:if>
<xsl:call-template name="removeSingles">
<xsl:with-param name="pText" select=
"substring-after($vText, ' ')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<title>
<article_title>Land a b c d Band</article_title>
</title>
produces the wanted, correct result:
<title>
<article_title>Land Band</article_title>
</title>