How to select element name, not value, using XSL/XSLT? - xslt

We are using XSL to convert a XML file into a pipe-delimited format.
<?xml version="1.0" encoding="UTF-8"?>
<ns:tradedata xmlns:ns="http://schemas.com/enterprise/util/extractservice/v1">
<tradedata_item>
<ORDER_ID>113632428</ORDER_ID>
<CUSIP>31393FHA7</CUSIP>
<TICKER>FHR</TICKER>
<SEC_NAME>FHR 2527 SG</SEC_NAME>
<ORDER_QTY>169249.6824</ORDER_QTY>
</tradedata_item>
<tradedata_item>
<ORDER_ID>113632434</ORDER_ID>
<CUSIP>31393G2C7</CUSIP>
<TICKER>FHR</TICKER>
<SEC_NAME>FHR 2531 ST</SEC_NAME>
<ORDER_QTY>214673.0105</ORDER_QTY>
</tradedata_item>
<tradedata_item>
<ORDER_ID>113632431</ORDER_ID>
<CUSIP>527069AH1</CUSIP>
<TICKER>LESL</TICKER>
<SEC_NAME>ZZZ_LESLIE S POOLMART INC</SEC_NAME>
<ORDER_QTY>365000.0000</ORDER_QTY>
</tradedata_item>
</ns:tradedata>
We need the first line in the output to be the column headers, and everything else would be data, like this...
ORDER_ID|CUSIP|TICKER|SEC_NAME|ORDER_QTY
1136324289|31393FHA7|FHR|FHR 2527 SG|169249.6824
1136324304|31393G2C7|FHR|FHR 2531 ST|214673.0105
We've got the XSL working to get the data, but we can't get the header to output correctly. We just select the first tradedata_item element, then iterate the element name and separate them using | characters. Here is the full XSL...
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="xsl"
version="1.0" xmlns="http://schemas.com/enterprise/util/extractservice/v1"
xmlns:o="http://schemas.com/enterprise/util/extractservice/v1" >
<!-- xsl:strip-space elements="*"/-->
<xsl:output method="text" indent="no"/>
<xsl:template match="/tradedata/tradedata_item[1]">
<xsl:for-each select="*">
<xsl:value-of select="local-name()"/>|
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="/">
<xsl:for-each select="tradedata/tradedata_item">
<xsl:value-of select="ORDER_ID"/>|<xsl:value-of select="CUSIP"/>|<xsl:value-of select="TICKER"/>|<xsl:value-of select="SEC_NAME"/>|<xsl:value-of select="ORDER_QTY"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The output we're seeing is just data, no header...
113632428|31393FHA7|FHR|FHR 2527 SG|169249.6824
113632430|31393G2C7|FHR|FHR 2531 ST|214673.0105
113632431|527069AH1|LESL|ZZZ_LESLIE S POOLMART INC|365000.0000
113632434|38470RAD3|GRAHAM|ZZZ_GRAHAM PACKAGING CO|595000.0000
Please disregard any namespace inconsistencies; I had to obfuscate the xml and xsl for legal reasons.

Try this :
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" indent="no"/>
<xsl:template match="/">
<xsl:for-each select="tradedata/tradedata_item[1]/*">
<xsl:value-of select="concat(name(), '|')"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Output :
ORDER_ID|CUSIP|TICKER|SEC_NAME|ORDER_QTY|
It seems pretty simple to me. Maybe your error lies elsewhere.

I tried your code.. I only changed the first template to match:
<xsl:template match="//tradedata_item[1]">
and it worked for me, i.e. got the header names.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="xsl ofi"
version="1.0" xmlns="http://schemas.com/enterprise/util/extractservice/v1"
xmlns:ofi="http://schemas.oppen.com/enterprise/util/extractservice/v1">
<xsl:output method="text" indent="no"/>
<xsl:template match="tradedata_item[position()='1']">
<xsl:for-each select="self::*">
<xsl:for-each select="child::*[position()!='5']">
<xsl:value-of select="local-name(self::*)"/>|
</xsl:for-each>
<xsl:value-of select="local-name(child::*[position()='5'])"/>
<xsl:text>
</xsl:text>
<xsl:value-of select="ORDER_ID"/>|<xsl:value-of select="CUSIP"/>|<xsl:value-of select="TICKER"/>|<xsl:value-of select="SEC_NAME"/>|<xsl:value-of select="ORDER_QTY"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template match="tradedata_item[position()>1]">
<xsl:for-each select="self::*">
<xsl:value-of select="ORDER_ID"/>|<xsl:value-of select="CUSIP"/>|<xsl:value-of select="TICKER"/>|<xsl:value-of select="SEC_NAME"/>|<xsl:value-of select="ORDER_QTY"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Related

XSLT 1.0 - how to check when condition for string

I am trying to conditional check on the input xml file and place the value.
input xml:
<workorder>
<newwo>1</newwo>
</workorder>
If newwo is 1, then I have to set in my output as "NEW" else "OLD"
Expected output is:
newwo: "NEW"
my xslt is:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
<xsl:template match="/">
<xsl:apply-templates select="NEWWO" />
</xsl:template>
<xsl:template match="/NEWWO">
<xsl:text>{
newwo:"
</xsl:text>
<xsl:choose>
<xsl:when test="NEWWO != '0'">NEW</xsl:when>
<xsl:otherwise>OLD</xsl:otherwise>
</xsl:choose>
<xsl:text>"
}</xsl:text>
</xsl:template>
Please help me. Thanks in advance!
I see a number of reasons you aren't getting output.
The xpaths are case sensitive. NEWWO is not going to match newwo.
You match / and then apply-templates to newwo (case fixed), but newwo doesn't exist at that context. You'll either have to add */ or workorder/ to the apply-templates (like select="*/newwo") or change / to /* or /workorder in the match.
You match /newwo (case fixed again), but newwo is not the root element. Remove the /.
You do the following test: test="newwo != '0'", but newwo is already the current context. Use . or normalize-space() instead. (If you use normalize-space(), be sure to test against a string. (Quote the 1.))
Here's an updated example.
XML Input
<workorder>
<newwo>1</newwo>
</workorder>
XSLT 1.0
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text"/>
<xsl:template match="/*">
<xsl:apply-templates select="newwo" />
</xsl:template>
<xsl:template match="newwo">
<xsl:text>{
newwo: "</xsl:text>
<xsl:choose>
<xsl:when test=".=1">NEW</xsl:when>
<xsl:otherwise>OLD</xsl:otherwise>
</xsl:choose>
<xsl:text>"
}</xsl:text>
</xsl:template>
</xsl:stylesheet>
Output
{
newwo: "NEW"
}
You try it as below
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.0">
<xsl:template match="/">
<xsl:choose>
<xsl:when test="/workorder/newwo = 1">
<xsl:text disable-output-escaping="no"> newwo:New</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text disable-output-escaping="no"> newwo:Old</xsl:text> </xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>

Sort the info based on given sequence order

Please suggest how to sort the info, based on given sort sequence (Seq.xml).
Here fetching info from folder where files' name should have 'Sort##.xml' formats. Output should be sort based on DOI number as sequnce given in external file 'D:\Sort\Seq.xml'.
Input XMLs:
D:\Sort\Sort01.xml
<article>
<fm>
<title>The solar system</title>
<aug><au>Rudramuni TP</au></aug>
<doi>10.11/MPS.0.10.11</doi>
</fm>
<body><text>The text</text></body>
</article>
D:\Sort\Sort02.xml
<article>
<fm>
<title>The Galaxy</title>
<aug><au>Kishan TR</au></aug>
<doi>10.11/MPS.0.10.2</doi>
</fm>
<body><text>The text</text></body>
</article>
D:\Sort\Sort03.xml
<article>
<fm>
<title>The Pluto</title>
<aug><au>Kowshik MD</au></aug>
<doi>10.11/MPS.0.10.10</doi>
</fm>
<body><text>The text</text></body>
</article>
Sequence info in D:\Sort\Seq.xml
<root>
<toc>
<seq seq="1"><art-id>10.11/MPS.0.10.2</art-id></seq>
<seq seq="2"><art-id>10.11/MPS.0.10.11</art-id></seq>
<seq seq="3"><art-id>10.11/MPS.0.10.10</art-id></seq>
</toc>
</root>
XSLT:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:variable name="varCollection">
<xsl:copy-of select="
collection('file:///D:/Sort/?select=Sort*.xml; recurse=yes')
[matches(document-uri(.),'Sort/Sort[0-9][0-9].xml')]"/>
</xsl:variable><!-- to fetch info from folder 'Sort*.xml' s -->
<xsl:variable name="docSeq" select="document('D:/Sort/Seq.xml')"/><!--In this file, sequnce info is there -->
<!--xsl:key name="kSeq" match="$docSeq/root/toc/seq/#seq" use="art-id"/--><!-- I tried with key, but unable to get the required sequence -->
<xsl:template match="root">
<xsl:for-each select="$varCollection/article">
<!--xsl:sort select="key('kSeq', fm/doi)"/-->
<art>
<title><xsl:value-of select="fm/title"/></title>
<Name><xsl:value-of select="fm/aug/au"/></Name>
<DOI><xsl:value-of select="fm/doi"/></DOI>
</art><xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Required Sequence
<art><title>The Galaxy</title><Name>Kishan TR</Name><DOI>10.11/MPS.0.10.2</DOI></art>
<art><title>The solar system</title><Name>Rudramuni TP</Name><DOI>10.11/MPS.0.10.11</DOI></art>
<art><title>The Pluto</title><Name>Kowshik MD</Name><DOI>10.11/MPS.0.10.10</DOI></art>
I think you want
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="varCollection" select="
collection('file:///D:/Sort/?select=Sort*.xml; recurse=yes')
[matches(document-uri(.),'Sort/Sort[0-9][0-9].xml')]"/>
<xsl:variable name="docSeq" select="document('file:///D:/Sort/Seq.xml')"/><!--In this file, sequnce info is there -->
<xsl:key name="kSeq" match="root/toc/seq" use="art-id"/>
<xsl:template match="root">
<xsl:for-each select="$varCollection/article">
<xsl:sort select="key('kSeq', fm/doi, $docSeq)/xs:integer(#seq)"/>
<art>
<title><xsl:value-of select="fm/title"/></title>
<Name><xsl:value-of select="fm/aug/au"/></Name>
<DOI><xsl:value-of select="fm/doi"/></DOI>
</art>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
You could also do this from the opposite direction:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:variable name="varCollection">
<xsl:sequence select="collection('file:///D:/Sort/?select=Sort*.xml; recurse=yes')
[matches(document-uri(.),'Sort/Sort[0-9][0-9].xml')]"/>
</xsl:variable>
<xsl:variable name="docSeq" select="document('D:/Sort/Seq.xml')"/>
<xsl:key name="article" match="article" use="fm/doi" />
<xsl:template match="root">
<xsl:for-each select="$docSeq/root/toc/seq">
<xsl:sort select="#seq" data-type="number" order="ascending"/>
<xsl:apply-templates select="key('article', art-id, $varCollection)"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="article">
<art>
<title><xsl:value-of select="fm/title"/></title>
<Name><xsl:value-of select="fm/aug/au"/></Name>
<DOI><xsl:value-of select="fm/doi"/></DOI>
</art>
</xsl:template>
</xsl:stylesheet>
Note that the output lacks a root element.

How too make if else check using boolen in XSLT (transunion)

I have xml like this
<fileSummary>
<fileHitIndicator>regularHit</fileHitIndicator>
<ssnMatchIndicator>noMatch</ssnMatchIndicator>
<consumerStatementIndicator>true</consumerStatementIndicator>
<market>32</market>
<submarket>QU</submarket>
<creditDataStatus>
<suppressed>false</suppressed>
<doNotPromote>
<indicator>false</indicator>
</doNotPromote>
<freeze>
<indicator>false</indicator>
</freeze>
<minor>false</minor>
<disputed>false</disputed>
</creditDataStatus>
<inFileSinceDate estimatedCentury="false" estimatedDay="false" estimatedMonth="false" estimatedYear="false">2004-02-02</inFileSinceDate>
</fileSummary>
I want to make check if indicator value is true then show some text otherwise hide it.
<freeze>
<indicator>false</indicator>
</freeze>
I am new to XSLT, please let me know.
If the description of what you want to achieve includes an "otherwise", then you're not in need of an xsl:if, but an xsl:choose.
In the example below it is crucial that the template matches the freeze element, i.e. that the context of the xsl:choose is the freeze element.
EDIT: Added a complete example based on your updated input.
Stylesheet
<?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"/>
<xsl:template match="freeze">
<result>
<xsl:choose>
<xsl:when test="indicator = 'false'">
<xsl:text>Indicator is false!</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>Indicator is not false!</xsl:text>
</xsl:otherwise>
</xsl:choose>
</result>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
Output
<?xml version="1.0" encoding="utf-8"?>
<result>Indicator is false!</result>
The stylesheet below:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="indicator">
<xsl:choose>
<xsl:when test=".='true'">
<xsl:text>some text</xsl:text>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
when applied to a your input (slightly modified)
<?xml version="1.0" encoding="UTF-8"?>
<fileSummary>
<fileHitIndicator>regularHit</fileHitIndicator>
<ssnMatchIndicator>noMatch</ssnMatchIndicator>
<consumerStatementIndicator>true</consumerStatementIndicator>
<market>32</market>
<submarket>QU</submarket>
<creditDataStatus>
<suppressed>false</suppressed>
<doNotPromote>
<indicator>true</indicator>
</doNotPromote>
<freeze>
<indicator>false</indicator>
</freeze>
<minor>false</minor>
<disputed>false</disputed>
</creditDataStatus>
<inFileSinceDate estimatedCentury="false" estimatedDay="false" estimatedMonth="false" estimatedYear="false">2004-02-02</inFileSinceDate>
</fileSummary>
produces:
<?xml version="1.0" encoding="utf-8"?><fileSummary>
<fileHitIndicator>regularHit</fileHitIndicator>
<ssnMatchIndicator>noMatch</ssnMatchIndicator>
<consumerStatementIndicator>true</consumerStatementIndicator>
<market>32</market>
<submarket>QU</submarket>
<creditDataStatus>
<suppressed>false</suppressed>
<doNotPromote>
some text
</doNotPromote>
<freeze>
</freeze>
<minor>false</minor>
<disputed>false</disputed>
</creditDataStatus>
<inFileSinceDate estimatedCentury="false" estimatedDay="false" estimatedMonth="false" estimatedYear="false">2004-02-02</inFileSinceDate>
</fileSummary>

Text value of input xml element as final xslt output

I have a scenario where the input(source) xml is having an element which contains a valid well formed xml as string. I am trying to write an xslt that would give me the text value of that desired element which contains the payload xml. In essence, output should only be text of the element that contains it. Here is what I am trying, am I missing something obvious here. I am using xslt 1.0
Thanks.
Input xml:
<BatchOrders xmlns="http://Microsoft.ABCD.OracleDB/STMT">
<BatchOrdersRECORD>
<BatchOrdersRECORD>
<ActualPayload>
<PersonName>
<PersonGivenName>CaptainJack</PersonGivenName>
<PersonMiddleName>Walter</PersonMiddleName>
<PersonSurName>Sparrow</PersonSurName>
<PersonNameSuffixText>Sr.</PersonNameSuffixText>
</PersonName>
</ActualPayload>
</BatchOrdersRECORD>
</BatchOrdersRECORD>
</BatchOrders>
Xslt:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="text()|#*" name="sourcecopy" mode="xml-to-string">
<xsl:value-of select="*"/>
</xsl:template>
<xsl:template name="xml-to-string-called-template">
<xsl:param name ="param1">
<xsl:element name ="DestPayload">
<xsl:text disable-output-escaping ="yes"><![CDATA[</xsl:text>
<xsl:call-template name ="sourcecopy"/>
<xsl:text disable-output-escaping ="yes">]]></xsl:text>
</xsl:element>
</xsl:param>
</xsl:template>
</xsl:stylesheet>
Desired Output:
<PersonName>
<PersonGivenName>CaptainJack</PersonGivenName>
<PersonMiddleName>Walter</PersonMiddleName>
<PersonSurName>Sparrow</PersonSurName>
<PersonNameSuffixText>Sr.</PersonNameSuffixText>
</PersonName>
Do you really need the mode="xml-to-string"?
Change
<xsl:template match="text()|#*" name="sourcecopy" mode="xml-to-string">
<xsl:value-of select="*"/>
</xsl:template>
to
<xsl:template match="text()|#*" name="sourcecopy">
<xsl:value-of select="." disable-output-escaping ="yes"/>
</xsl:template>
Would this template suffice?

XSL associative sorting using a field substring

The transformation I am writing must compose a comma separated string value from a given node set. The resulting string must be sorted according to a random (non-alphabetic) mapping for the first character in the input values.
I came up with this:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:tmp="http://tempuri.org"
exclude-result-prefixes="tmp"
>
<xsl:output method="xml" indent="yes"/>
<tmp:sorting-criterion>
<code value="A">5</code>
<code value="B">1</code>
<code value="C">3</code>
</tmp:sorting-criterion>
<xsl:template match="/InputValueParentNode">
<xsl:element name="OutputValues">
<xsl:for-each select="InputValue">
<xsl:sort select="document('')/*/tmp:sorting-criterion/code[#value=substring(.,1,1)]" data-type="number"/>
<xsl:value-of select="normalize-space(.)"/>
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
It doesn't work and looks like the XPath document('')/*/tmp:sorting-criterion/code[#value=substring(.,1,1)] does not evaluate as I expect. I've checked to substitute the substring(.,1,1) for a literal and it evaluates to the proper value.
So, am I missing something that makes the sorting XPath expression not to evaluate as I expect or is it simply impossile to do it this way?
If not possible to create a XPath expression that works, is there a work around to achieve my purpose?
Note: I'm constrained to XSLT-1.0
Sample Input:
<?xml version="1.0" encoding="utf-8"?>
<InputValueParentNode>
<InputValue>A input value</InputValue>
<InputValue>B input value</InputValue>
<InputValue>C input value</InputValue>
</InputValueParentNode>
Expected ouput:
<?xml version="1.0" encoding="utf-8"?>
<OutputValues>B input value,C input value,A input value</OutputValues>
Replace the self::node() abbreviation ., with current() function.
A better predicate would be: starts-with(normalize-space(current()),#value)
Besides changing transformation according to Alejandro´s answer, I found it better to use a XSL variable for th mapping data to avoid declaration of a dummy namespace (tmp) as seen in Dimitre´s answer to another related question.
My final implementation:
<?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"/>
<xsl:template match="/InputValueParentNode">
<xsl:variable name="sorting-map">
<i code="A" priority="5"/>
<i code="B" priority="1"/>
<i code="C" priority="3"/>
</xsl:variable>
<xsl:variable name="sorting-criterion" select="document('')//xsl:variable[#name='sorting-map']/*"/>
<xsl:element name="OutputValues">
<xsl:for-each select="InputValue">
<xsl:sort select="$sorting-criterion[#code=substring(normalize-space(current()),1,1)]/#priority" data-type="number"/>
<xsl:value-of select="normalize-space(current())"/>
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>