Reference namespace in same XSLT - xslt

Trying to reference namespace to the same xslt with document('') but I get :
SystemID: file:/c:/intersystems/cache/mgr/samples/; Line#: 1; Column#: 1
net.sf.saxon.trans.XPathException: Error reported by XML parser
....
Caused by: org.xml.sax.SAXParseException; systemId: file:/c:/intersystems/cache/mgr/samples/; lineNumber: 1; columnNumber: 1; Content is not allowed in prolog.
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(Unknown Source)
So it seems can not reference the same xslt? Is there some way to do this
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:xdt="http://www.w3.org/2005/xpath-datatypes" xmlns:csv="csv:csv">
<!-- <xsl:output method="text" version="4.0" encoding="iso-8859-1" indent="yes"/> -->
<xsl:strip-space elements="*" />
<xsl:output method="text" encoding="utf-8" />
<xsl:variable name="delimiter" select="','"/>
<csv:columns>
<column>GlobalID</column>
<column>ServicePointName</column>
</csv:columns>
<xsl:template match="/Report">
<!-- Output the CSV header -->
****<xsl:for-each select="document('')/*/csv:columns/*">****
<xsl:value-of select="."/>
<xsl:if test="position() != last()">
<xsl:value-of select="$delimiter"/>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
<!-- Output rows for each matched Report -->
<xsl:apply-templates select="*" />
</xsl:template>
<xsl:template match="/Report/CLI">
<xsl:for-each select="//CLI">
<xsl:value-of select="GlobalID"/>
<xsl:value-of select="$delimiter"/>
<xsl:value-of select="ServicePointName"/>
<!-- Add a newline at the end of the record -->
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Saxon 9 is an XSLT 2.0 processor - so you can easily do:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:variable name="delimiter" select="','"/>
<xsl:variable name="columns">
<column>GlobalID</column>
<column>ServicePointName</column>
</xsl:variable>
<xsl:template match="/">
<xsl:for-each select="$columns/*">
<xsl:value-of select="."/>
<xsl:if test="position() != last()">
<xsl:value-of select="$delimiter"/>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
<!-- the rest -->
</xsl:template>
</xsl:stylesheet>
I don't see that you're using the columns further on in your stylesheet. If so, why don't you simplify the whole thing to:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="/">
<xsl:text>GlobalID,ServicePointName
</xsl:text>
<!-- the rest -->
</xsl:template>
</xsl:stylesheet>
(which is also XSLT 1.0 compatible).
None of this explains why your attempt to use an internal node wouldn't work.

I've never tried to do this, but just looking at the documentation for the document() function it says this:
document() Used to access the nodes in an external XML document
Edit: I jumped the gun as pointed out by comments below. There is another question facing the same problems:
XSLT document('') function doesn't work

Related

XSLT 2.0 get distinct nodes names and preserve order

I am trying to transform XML to CSV where each entry does not contain all values. The column order must be preserved.
Initial file:
<?xml version='1.0' encoding='UTF-8'?>
<data>
<entry>
<a>FR</a>
<b>Dupont</b>
<c>123456</c>
<d>zzz</d>
<f>New York</f>
</entry>
<entry>
<a>FR</a>
<b>Martin</b>
<c>234561</c>
<d>xxx</d>
<e>2019-01-01</e>
<f>Paris</f>
</entry>
<entry>
<a>FR</a>
<b>Chris</b>
<c>345612</c>
<d>yyy</d>
<e>2019-01-01</e>
</entry>
</data>
Expected output:
a;b;c;d;e;f
FR;Dupont;123456;zzz;;New York
FR;Martin;234561;xxx;2019-01-01;Paris
FR;Chris;345612;yyy;2019-01-01;
I am struggling with getting the header values in the correct order. I tried distinct-values() and for-each-group() but I am not able to preserve the order.
One example of what I tried:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
exclude-result-prefixes="#all">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:for-each select="distinct-values(//entry/*/local-name())">
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Output:
abcdfe
Any ideas? Thanks.
I would use the following approach:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="cols"
as="xs:string*"
select="let $max-cols := max(data/entry/count(*))
return distinct-values(data/entry[count(*) = $max-cols]/*/local-name())"/>
<xsl:template match="data">
<xsl:value-of select="$cols" separator=";"/>
<xsl:text>
</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="entry">
<xsl:value-of
select="for $col in $cols
return string(*[local-name() = $col])"
separator=";"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/bFWR5Em/1
Try this (updated):
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="#all">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:variable name="cols" as="element()*">
<xsl:for-each select="distinct-values(//entry/*/local-name())">
<xsl:sort select="."/>
<Item><xsl:value-of select="."/></Item>
</xsl:for-each>
</xsl:variable>
<xsl:for-each select="$cols">
<xsl:value-of select="concat(.,';')"/>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:for-each select="//entry">
<xsl:variable name="entry" select="."/>
<xsl:for-each select="$cols">
<xsl:value-of select="concat($entry/*[local-name() = current()], ';')"/>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

correct xsl template matching syntax

I'm learning XSL and hope to get some help. I want to extract part of the following datasets.xml and output them as tab delimited texts:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<listDatasetsResponse xmlns="http://www.algorithmics.com/schema">
<status>OK</status>
<datasets size="31">
<dataset>
<id>stress_20150910_20150910_160259</id>
<basedOn>Mimm_20150910_20150910_030922</basedOn>
<active>false</active>
<sandbox>true</sandbox>
<ownedBy>admin</ownedBy>
<createdOn>2015-09-10T16:04:24.199-04:00</createdOn>
<createdBy>rtcesupp</createdBy>
<evaluated>true</evaluated>
<stopped>false</stopped>
</dataset>
<dataset>
<id>imm_20150910_20150910_140315</id>
<basedOn>Mimm_20150910_20150910_030922</basedOn>
<active>true</active>
<sandbox>true</sandbox>
<ownedBy>admin</ownedBy>
<createdOn>2015-09-10T14:04:42.696-04:00</createdOn>
<createdBy>rtcesupp</createdBy>
<evaluated>true</evaluated>
<stopped>false</stopped>
</dataset>
</datasets>
</listDatasetsResponse>
here is the XSL I used:
$ vi dataset.xsl
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="iso-8859-1"/>
<xsl:strip-space elements="*" />
<xsl:template match="/listDatasetsResponse/datasets/dataset">
<xsl:value-of select="id"/><xsl:text> </xsl:text>
<xsl:choose>
<xsl:when test="active='true'">
<xsl:text>ACTIVE</xsl:text>
</xsl:when>
<xsl:when test="stopped='true'">
<xsl:text>,STOPPED</xsl:text>
</xsl:when>
<xsl:when test="evaluated='true'">
<xsl:text>,EVALUATED</xsl:text>
</xsl:when>
</xsl:choose>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
I expected the result of:
stress_20150910_20150910_160259 EVALUATED
imm_20150910_20150910_140315 ACTIVE,EVALUATED
but what I got is:
OKstress_20150910_20150910_160259Mimm_20150910_20150910_030922falsetrueadmin2015-09-10T16:04:24.199-04:00rtcesupptruefalseimm_20150910_20150910_140315Mimm_20150910_20150910_030922truetrueadmin2015-09-10T14:04:42.696-04:00rtcesupptruefalse
It seemed like the XSL stylesheet was ignored. Could some one point me to correct XSL template matching syntax?
The fundamental problem in your XSL is that none of the XPath expression being used match the element in the source XML. Notice your XML has default namespace declared at the root element :
xmlns="http://www.algorithmics.com/schema"
Descendant elements without explicit prefix and without local default namespace inherits ancestor default namespace implicitly. To match element in namespace, simply declare a prefix that point to the namespace-uri and use that prefix in your XPath expressions, for example :
<xsl:stylesheet .....
xmlns:d="http://www.algorithmics.com/schema">
.....
<xsl:template match="/d:listDatasetsResponse/d:datasets/d:dataset">
<xsl:value-of select="d:id"/><xsl:text> </xsl:text>
.....
</xsl:template>
</xsl:stylesheet>
How about ...
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:a="http://www.algorithmics.com/schema"
version="1.0" >
<xsl:output method="text" encoding="utf-8"/>
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()" />
<xsl:template match="/">
<xsl:apply-templates select="a:listDatasetsResponse/a:datasets/a:dataset" />
</xsl:template>
<xsl:template match="a:dataset">
<xsl:value-of select="a:id"/>
<xsl:text> </xsl:text>
<xsl:apply-templates select="a:active|a:stopped|a:evaluated" />
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="a:active[.='true']">
<xsl:if test="preceding-sibling::a:active[.='true']|
preceding-sibling::a:stopped[.='true']|
preceding-sibling::a:evaluated[.='true']">,</xsl:if>
<xsl:text>ACTIVE</xsl:text>
</xsl:template>
<xsl:template match="a:stopped[.='true']">
<xsl:if test="preceding-sibling::a:active[.='true']|
preceding-sibling::a:stopped[.='true']|
preceding-sibling::a:evaluated[.='true']">,</xsl:if>
<xsl:text>STOPPED</xsl:text>
</xsl:template>
<xsl:template match="a:evaluated[.='true']">
<xsl:if test="preceding-sibling::a:active[.='true']|
preceding-sibling::a:stopped[.='true']|
preceding-sibling::a:evaluated[.='true']">,</xsl:if>
<xsl:text>EVALUATED</xsl:text>
</xsl:template>
</xsl:stylesheet>
... or this version ...
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:a="http://www.algorithmics.com/schema"
version="1.0" >
<xsl:output method="text" encoding="utf-8"/>
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()" />
<xsl:template match="/">
<xsl:apply-templates select="a:listDatasetsResponse/a:datasets/a:dataset" />
</xsl:template>
<xsl:template match="a:dataset">
<xsl:value-of select="a:id"/>
<xsl:text> </xsl:text>
<xsl:apply-templates select="a:active|a:stopped|a:evaluated" />
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="a:active[.='true'] | a:stopped[.='true'] | a:evaluated[.='true']">
<xsl:if test="preceding-sibling::a:active[.='true']|
preceding-sibling::a:stopped[.='true']|
preceding-sibling::a:evaluated[.='true']">,</xsl:if>
<xsl:value-of select="translate( local-name(), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
</xsl:template>
</xsl:stylesheet>

XSLT manipulate text scattered across various nodes

Input file as follows:
<?xml version="1.0" encoding="UTF-8"?>
<!-- lower UPPER case -->
<document>
<rubbish> rubbish </rubbish>
<span class='lower'>
lower
<span class='upper'> upper </span>
case
</span>
</document>
Wanted output:
lower UPPER case
I know how to get the text included in the outer span with value-of, but this also
includes the string "upper" unchanged which is not what I want. I do not know how
to manipulate the text in the inner span and insert it in the middle of
the other text.
Failed attempt:
<?xml version="1.0" encoding="UTF-8"?>
<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="//span[#class = 'lower']">
<xsl:if test="span/#class = 'upper'">
<xsl:text>do something</xsl:text> <!--TO DO -->
</xsl:if>
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
You need to take a recursive approach here, for example:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="text()[parent::span]">
<xsl:choose>
<xsl:when test="../#class='upper'">
<xsl:value-of select="translate(., 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
To understand how this works, read up on built-in template rules: http://www.w3.org/TR/xslt/#built-in-rule
The following approach does away with the <choose> and completely pushes the problem down to the match expression:
<?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="text()"/>
<xsl:template match="text()[parent::span[#class = 'upper']]">
<xsl:value-of select="translate(., 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
</xsl:template>
<xsl:template match="text()[parent::span[#class = 'lower']]">
<xsl:value-of select="translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>
</xsl:template>
</xsl:stylesheet>

how to control multiple tags not neccessary that to repeat again

here i m target a data with table of CTD_CTD_PKG_ID in my xslt but some tags were having data pretty same that type of tags we have to control not to repeat once more
<?xml version="1.0" standalone="yes"?>
<NewDataSet>
<Table>
<RECORD_TYPE_CODE>CTD</RECORD_TYPE_CODE>
<MSG_TYPE_CODE>O102</MSG_TYPE_CODE>
<CTD_SEQ_NUM>089938</CTD_SEQ_NUM>
<CTD_CTD_PKG_ID>345</CTD_CTD_PKG_ID>
<CTD_LANG_ID>E</CTD_LANG_ID>
</Table>
<Table>
<RECORD_TYPE_CODE>ITD</RECORD_TYPE_CODE>
<MSG_TYPE_CODE>O103</MSG_TYPE_CODE>
<CTD_SEQ_NUM>089939</CTD_SEQ_NUM>
<CTD_CTD_PKG_ID>345</CTD_CTD_PKG_ID>
<CTD_LANG_ID>E</CTD_LANG_ID>
</Table>
</NewDataSet>
i have written my xslt below
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no" omit-xml-declaration="yes" />
<xsl:param name="PackageId" />
<xsl:template match="/">
<xsl:apply-templates select="NewDataSet/Table[CTD_CTD_PKG_ID ='345']"/>
</xsl:template>
<xsl:template match="NewDataSet/Table[CTD_CTD_PKG_ID ='345']">
<xsl:value-of select= "concat(':25:',./CTD_LANG_ID)"/>,<xsl:text/>
<xsl:if test ="./RECORD_TYPE_CODE" >
<xsl:if test=" position() > 1"></xsl:if>
<xsl:text/><xsl:value-of select= "concat(':20:',./RECORD_TYPE_CODE)" />,<xsl:text/>
</xsl:if>
<xsl:if test ="./MSG_TYPE_CODE" >
<xsl:if test=" position() > 1"></xsl:if>
<xsl:text/><xsl:value-of select= "concat(':21:',./MSG_TYPE_CODE)"/>,<xsl:text/>
</xsl:if>
<xsl:if test ="./CTD_SEQ_NUM" >
<xsl:if test=" position() > 1"></xsl:if>
<xsl:text/><xsl:value-of select= "concat(':22:',./CTD_SEQ_NUM)"/>,<xsl:text/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
current output for this above xslt
:25:E,:25:E,
Expected output=
:25:E,
If you are using such specific IDs, a simple way to do this would be to simply select the first TABLE with the matching ID using the position() function, like so
<xsl:apply-templates select="NewDataSet/Table[CTD_CTD_PKG_ID ='345'][position()=1]"/>
Note that, you can actually simplify the template match by just matching on Table if required. Here is the whole XSLT in this case
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no" omit-xml-declaration="yes"/>
<xsl:param name="PackageId"/>
<xsl:template match="/">
<xsl:apply-templates select="NewDataSet/Table[CTD_CTD_PKG_ID ='345'][position()=1]"/>
</xsl:template>
<xsl:template match="Table">
<xsl:value-of select="concat(':25:',./CTD_LANG_ID)"/>,
<xsl:text/></xsl:template>
</xsl:stylesheet>
When applied on your input XML, the result is as follows:
:25:E,

XSLT: How to reverse output without sorting by content

I have a list of items:
<item>a</item>
<item>x</item>
<item>c</item>
<item>z</item>
and I want as output
z
c
x
a
I have no order information in the file and I just want to reverse the lines. The last line in the source file should be first line in the output. How can I solve this problem with XSLT without sorting by the content of the items, which would give the wrong result?
I will present two XSLT solutions:
I. XSLT 1.0 with recursion Note that this solution works for any node-set, not only in the case when the nodes are siblings:
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:call-template name="reverse">
<xsl:with-param name="pList" select="*"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="reverse">
<xsl:param name="pList"/>
<xsl:if test="$pList">
<xsl:value-of
select="concat($pList[last()], '
')"/>
<xsl:call-template name="reverse">
<xsl:with-param name="pList"
select="$pList[not(position() = last())]"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<t>
<item>a</item>
<item>x</item>
<item>c</item>
<item>z</item>
</t>
produces the wanted result:
z
c
x
a
II. XSLT 2.0 solution :
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="/*">
<xsl:value-of select="reverse(*)/string(.)"
separator="
"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the same XML document, the same correct result is produced.
XML CODE:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<device>
<element>a</element>
<element>x</element>
<element>c</element>
<element>z</element>
</device>
XSLT CODE:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="//device">
<xsl:for-each select="element">
<xsl:sort select="position()" data-type="number" order="descending"/>
<xsl:text> </xsl:text>
<xsl:value-of select="."/>
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:template>
note: if you're using data-type="number", and any of the values aren't numbers, those non-numeric values will sort before the numeric values. That means if you're using order="ascending", the non-numeric values appear first; if you use order="descending", the non-numeric values appear last.
Notice that the non-numeric values were not sorted; they simply appear in the output document in the order in which they were encountered.
also, you may find usefull to read this:
http://docstore.mik.ua/orelly/xml/xslt/ch06_01.htm
Not sure what the full XML looks like, so I wrapped in a <doc> element to make it well formed:
<doc>
<item>a</item>
<item>x</item>
<item>c</item>
<item>z</item>
</doc>
Running that example XML against this 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" encoding="UTF-8" omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:call-template name="reverse">
<xsl:with-param name="item" select="doc/item[position()=last()]" />
</xsl:call-template>
</xsl:template>
<xsl:template name="reverse">
<xsl:param name="item" />
<xsl:value-of select="$item" />
<!--Adds a line feed-->
<xsl:text>
</xsl:text>
<!--Move on to the next item, if we aren't at the first-->
<xsl:if test="$item/preceding-sibling::item">
<xsl:call-template name="reverse">
<xsl:with-param name="item" select="$item/preceding-sibling::item[1]" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Produces the requested output:
z
c
x
a
You may need to adjust the xpath to match your actual XML.
Consider this XML input:
<?xml version="1.0" encoding="utf-8" ?>
<items>
<item>a</item>
<item>x</item>
<item>c</item>
<item>z</item>
</items>
The XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:template match="/items[1]">
<xsl:variable name="items-list" select="." />
<xsl:variable name="items-count" select="count($items-list/*)" />
<xsl:for-each select="item">
<xsl:variable name="index" select="$items-count+1 - position()"/>
<xsl:value-of select="$items-list/item[$index]"/>
<xsl:value-of select="'
'"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
And the result:
z
c
x
a