Concatenate to existing value in xlst - xslt-1.0

With XSLT 1.0 I simply want to add a negative sign before the value in the Identifier element. The stylesheet I have shown below returns the same (i.e. the first instance) Identifier value for each instance of RToP. There can be more than one RToP. PLease advise how to modify the match so I return the desired output below. Thanks. I'm omitting the namespace in the xml below.
<TRAN><DE><REF>
<RToP><Identifier>100</Identifier>
</RToP>
<RToP><Identifier>150</Identifier>
</RToP>
</TRAN></DE></REF>
Existing output
<TRAN><DE><REF>
<RToP><Identifier>-100</Identifier>
</RToP>
<RToP><Identifier>-100</Identifier>
</RToP>
</TRAN></DE></REF>
Desired output
<TRAN><DE><REF>
<RToP><Identifier>-100</Identifier>
</RToP>
<RToP><Identifier>-150</Identifier>
</RToP>
</TRAN></DE></REF>
Existing stylesheet
<xsl:template match="//my:TRAN/my:DE/my:REF/my:RToP/my:Identifier[position()]">
<my:Identifier>
<xsl:value-of select="concat('-', //my:TRAN/my:DE/my:REF/my:RToP/my:Identifier[position()])"/>
</my:Identifier>
</xsl:template>

For this type of transformation you need to be use relative path instead of absolute path.
first you have to create at identical transformation to retain its structure.
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
than you have to define Identifier as template that run only on Identifier and there is no need to use predicates for that.
<xsl:template match="Identifier">
<Identifier>
<xsl:value-of select="concat('-', .)"/>
</Identifier>
</xsl:template>

Related

How to put 'and' condition to remove an xml element from an xslt

I am new to XSLT and got a condition to remove an element from XML file on the basis of condition applied in XSLT. I have to remove en element on the basis of 2 conditions on the same attribute.
Here is my dummy code:
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="cdm:attributeList/cdm:attribute[cdm:attributeName = 'format'] and cdm:attributeList/cdm:attribute[cdm:attributeValue = 'XYZ']" />
while running the XSLT file I get the following error:
1. Extra illegal tokens: 'and'
2. "exclude-result-prefixes" attribute is not allowed on the xsl:output element!
Could someone please help me out in this?
Thanks.
The template matching condition is incorrect, and operator is not allowed when matching template. You need to modify the template matching condition as below which corresponds to the combination of the required conditions.
<xsl:template match="cdm:attributeList/cdm:attribute[cdm:attributeName = 'format'][cdm:attributeValue = 'XYZ']" />
For the second issue of exclude-result-prefixes, it is an attribute of <xsl:stylesheet> and not <xsl:output>. So if you do not want the output nodes to have the cdm prefix, modify the <xsl:stylesheet> as below
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:cdm="http://someurl" exclude-result-prefixes="cdm">

BizTalk Mapping Single and Multiple elements

I am trying to transform via the map a single birth name node and multiple surname nodes into a repeating other surname nodes. I'm running into some difficulties that when the birth name node is not present then multiple surname nodes fail to be written.
I've attempted multiple implementations around functoids and xslt call template neither appear to be working, as soon as the birth name is missing no surname elements are output.
Can this be done in functoids from the map ? or does this have to be done via a xslt call template?
Schema Input
<root>
<Subject>
<birthname>
<name>Birthname</name>
</birthname>
<multiplesurname>
<name>surname</name>
</multiplesurname>
<multiplesurname>
<name>surname2</name>
</multiplesurname>
<multiplesurname>
<name>surname3</name>
</multiplesurname>
</Subject>
<Mother></Mother>
<Farther></Farther>
<Other></Other>
</root>
Schema Output
<root>
<persona>
<Othername>Birthname</Othername>
<Othername>surname</Othername>
<Othername>surname2</Othername>
<Othername>surname3</Othername>
</persona>
<personb></personb>
</root>
I think your problems may be caused by having a name node and then an descendant node also named name. This might be causing an infinite loop for you. Here is some XSLT code that will get the job done for you.
<xsl:template match="name">
<xsl:copy>
<xsl:apply-templates select=".//name" mode="secondName"/>
</xsl:copy>
</xsl:template>
<xsl:template match="name" mode="secondName">
<xsl:element name="Othername">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<!-- Identity. -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
You could probably do this with a table looper, but a XSLT call template for it shouldn't be too bad - something like this should work for you:
<xsl:template name="nameFlattener">
<xsl:param name="birthname"/>
<xsl:element name="Othername">
<xsl:value-of select="$birthname"/>
</xsl:element>
<xsl:for-each select="//multiplesurname">
<xsl:element name="Othername">
<xsl:value-of select="name"/>
</xsl:element>
</xsl:for-each>
</xsl:template>
Have your name node from birthname going into that template as the first parameter, and output it to the Othername repeating node on the destination.
After running into further difficulties with XSLT call-template I found that a solution using functoids was possible and achieved using a combination of looping functoids from the birthname and multiplesurname along with straight link from the source node to the destination node.

Looping a variable length array with namespaces in XSLT

My previous question[1] is related to this. I found the answer for that. Now I want to loop a variable length array with namespaces. My array:
<ns:array xmlns:ns="http://www.example.org">
<value>755</value>
<value>5861</value>
<value>4328</value>
<value>2157</value>
<value>1666</value>
</ns:array>
My XSLT code:(have added the namespace in the root)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns1="http://www.example.org">
<xsl:template match="/">
<xsl:variable name="number" select="ns:array" />
<xsl:for-each select="$number">
<xsl:value-of select="$number" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
[1]https://stackoverflow.com/questions/20287219/looping-a-variable-length-array-in-xslt
IMHO you confused yourself by introducing a variable called number which actually contains a node set of value tags. Then, as a consequence you used your variable as singe item/node which does not yield the desired result (presumingly, since you did not really tell us what you want to do with the values).
Also, I think your question does not really have anything to with namespace issues as such. You just have to make sure that the namespaces in your select expressions match the namespaces in your input file.
I would suggest to do without the variable and change the way you retrieve the current value:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns1="http://www.example.org">
<xsl:template match="/">
<xsl:for-each select="ns:array">
<!-- Inside here you can work with the `value` tag as the _current node_.
There are two most likely ways to do this. -->
<!-- a) Copy the whole tag to the output: -->
<xsl:copy-of select="." />
<!-- or b1) Copy the text part contained in the tag to the output: -->
<xsl:value-of select="." />
<!-- If you want to be on the safe side with respect to white space
you can also use this b2). This would handle the case that your output
is required not to have any white space in it but your imput XML has
some. -->
<xsl:value-of select="normalize-space(.)" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Numeric variable/parameter in XSLT

I have this XSLT:
<xsl:template match="/">
<xsl:variable name="errorCount" select="count($orders/*[1]/cm:Error)" />
<xsl:apply-templates select="#*|node()">
<xsl:with-param name="errorCount" select="$errorCount" tunnel="yes" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="status">
<xsl:param name="errorCount" tunnel="yes" />
<xsl:copy>
<xsl:choose>
<xsl:when test="$errorCount > 0">
<xsl:text>ERROR</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>OK</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
The tunneling and all seems to work, but the transformation fails with the following error:
Required item type of first operand of '>' is numeric; supplied value has item type xs:string
I first had the variable declaration in the template where it is it used, and then it worked fine. Moved it because I need to use the same count in other templates too.
How/Where do I declare that this variable/parameter is in fact a number?
Since you are using XSLT 2.0, then you should also add an as attribute to your xsl:param in the template. For example (you may have to use a different as value depending on what you need the resulting number to be, such as if you'll have values that have decimals; you would also need to correct the tunnel value, per Michael Kay's point):
<xsl:param name="errorCount" tunnel="yes" as="xs:integer" />
The transform would fail if it couldn't be converted to the as type (in this case, an integer). Eero's solution may look cleaner because you'll still have to check to see if the value is greater than zero. But, because you are using XSLT 2.0, the best practice is to type your parameters/variables.
You can use number() to convert a string to a number:
<xsl:when test="number($errorCount) > 0">
<xsl:text>ERROR</xsl:text>
</xsl:when>
My suspicion is that because you wrote tunnel="true" rather than tunnel="yes", the fact that you specified tunnel at all is being (incorrectly) ignored, and the parameter is being given its default value, which is a zero-length string.

Can disable-output-escaping be set using a template parameter?

Why won't the following work in XSLT1.0?
<xsl:template name="GenerateSummaryOld">
<xsl:param name="Content" />
<xsl:param name="Length" />
<xsl:param name="DisableOutputEscaping" />
<xsl:value-of select="substring($Content, 1, $Length)" disable-output-escaping="$DisableOutputEscaping" />
<xsl:if test="string-length($Content) > $Length"><i>...text has been shortened</i></xsl:if>
</xsl:template>
I'm using the following when calling the template:
<xsl:with-param name="DisableOutputEscaping">no</xsl:with-param>
I'm trying this in a SharePoint Content Query WebPart but I get a web part error. If I hard-code disable-output-escaping as "yes" or "no" in the template, i get no error.
Short answer: the value of disable-output-escaping must be specified literally in the XSLT stylesheet; it cannot be calculated at stylesheet execution time.
That is, the behavior you are observing is the behavior prescribed by the language definition.
Longer answer: The XSLT 1.0 spec shows the syntax of xsl:value-of like this (more or less):
<!-- Category: instruction -->
<xsl:value-of
select = string-expression
disable-output-escaping = "yes" | "no" />
Note that "string-expression" is italicized here; it means that the select attribute has as its value not the string "string-expression" but any XPath expression which can be evaluated and coerced to a string. But the "yes" and "no" of disable-output-escaping are not italicized, not described as being an expression, and not described as being an attribute-value template. The "yes" or "no" value must be given literally.
The closest the spec comes to saying this explicitly (that I could find) is the note in section 7.6.2 on attribute value templates:
NOTE:Not all attributes are interpreted as attribute value templates. Attributes whose value is an expression or pattern, attributes of top-level elements and attributes that refer to named XSLT objects are not interpreted as attribute value templates. ...
This is one of a number of early-binding constraints in XSLT designed to ensure that stylesheets could be compiled and not just interpreted.
The explanation was provided in the good answer by C. M. Sperberg-McQueen.
Here is a workaround:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vAmp">&</xsl:variable>
<xsl:variable name="vYesNo" select="'yes'"/>
<xsl:template match="/">
<xsl:choose>
<xsl:when test="$vYesNo = 'yes'">
<xsl:value-of select="$vAmp" disable-output-escaping="yes"/>
</xsl:when>
<xsl:when test="$vYesNo = 'no'">
<xsl:value-of select="$vAmp" disable-output-escaping="no"/>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on any XML document (not used), the result is:
&
If we replace:
<xsl:variable name="vYesNo" select="'yes'"/>
with:
<xsl:variable name="vYesNo" select="'no'"/>
the result of the modified transformation now is:
&