XSL - How to obtain the unix environment variable - xslt-2.0

I am trying to get the unparsed text and it will be stored in one of the unix directories whose path will change from one environment to the other. How can I obtain the base path dynamically using $ unix variables?
<fo:block xsl:use-attribute-sets="termcond">
<xsl:if test="($print_draft = '')">
<xsl:value-of select="unparsed-text('/reports/generic_tc.txt','UTF-8')"/>
</xsl:if>
</fo:block>
I want to be able to pass the $PO_TOP unix variable to (the below DOES NOT WORK)
<xsl:value-of select="unparsed-text('$PO_TOP/reports/generic_tc.txt','UTF-8')"/>
Thanks in advance

Related

straUnexpected output xsl:value-of

Havin a strange issue again. I am tryinodo a simple value- of of field CRETIM:
<CREDAT>20220628</CREDAT>
<CRETIM>112159</CRETIM>
<xsl:attribute name="timestamp">
<xsl:value-of select="EDI_DC40/CRETIM" disable-output-escaping="yes"/>
a
</xsl:attribute>
Output looks as follows:
First6 characterrs are ok, but no idea what happened to th rest.Any idea whats wrong here and how this can be fixed?
Thank you!
If that is XSLT 2 I wonder why you don't use the select attribute on xsl:attribute e.g. <xsl:attribute name="timestamp" select="EDI_DC40/CRETIM"/>.
If you use xsl:value-of wrapped into xsl:attribute make sure you have stray data (like that a letter) inside of xsl:attribute. If the sole a letter is intentional then use e.g. <xsl:attribute name="timestamp" select="concat(EDI_DC40/CRETIM, 'a')"/> or <xsl:attribute name="timestamp"><xsl:value-of select="EDI_DC40/CRETIM"/>a</xsl:attribute>.

xslt 2.0: read in text files via collection()

I have a bunch of text files that I'd like to process witth XSLT 2.0.
Here's how I try to read them in:
<xsl:variable name="input" select="collection(iri-to-uri('file:///.?select=*.txt'))" />
However, when I do this:
<xsl:message>
<xsl:sequence select="count($input)"/>
</xsl:message>
It outputs 0. No files are selected.
If I do it like this:
<xsl:variable name="input" select="collection(iri-to-uri('.?select=*.txt'))" />
I get the error that collection should return a node but is returning an xs:string.
What I would like do to is read each file and then iterate over each file and process the text, like this
<xsl:for-each select="unparsed-text($input, 'UTF-8')">
<!-- tokenizing, etc. -->
How would I do that?
You need the XPath 3.0 uri-collection function supported in version="3.0" stylesheet in Saxon 9.7 (all versions including HE) and 9.6 (commercial versions I think):
<xsl:template match="/" name="main">
<xsl:for-each select="uri-collection('.?select=*.txt')!unparsed-text(.)">
<xsl:message select="'Parsed:' || . || '
'"/>
</xsl:for-each>
</xsl:template>
collection is supposed to return a sequence of nodes while uri-collection can access other resources not parsable as XML.
With Altova XMLSpy respectively RaptorXML and XSLT 3.0 you can also use uri-collection, it seems the way to access all .txt files is a bit different from Saxon and you use uri-collection('*.txt') to access all .txt files in the directory.

xslt how to read the document-node()

I have a xml file in which one of the element has the CDATA as the value. I put the CDATA value into a variable which I can see is value type of document-node(1) when i debug my code from oXygen. How do I iterate the document-node()?
copy can give me a new xml file. but what I need is not a new file. I only need to read certain nodes and generate a report based on the values on those nodes. so I directly copy the CDATA to my variable and thought I can manipulate it.
I tried to use substring to read the variable things but failed.
I tried to use document(variable) to open the variable but Oxygen give me the debug-error of FODC0002:I/O error reported by xml parser processing file.
here the file is my variable which looks like a xml file
I did google search for the error but only got bench of non-closed questions like Oxygen throw I/O error when use document().
Would anybody let me know what's going wrong? or give me a better solution?
I also tried parse-xml() but I got the following error from Saxon:
F[Saxon-EE9.5.1.5] the processing instruction target matching "[xX][mM][lL]" is not allowed
F[Saxon-EE9.5.1.5] FODC0006: First argument to parse-xml() is not a well formed and namespace-well-formed XML document.
my code to use parse-xml is as below:
<xsl:template match="data"
<xsl:for-each select="parse-xml(root/outsideData)//nodeLevel1/nodeLevel2">
Could anyone give me a sample about how to use parse-xml()? I did google search but didn't find useful samples.
Thanks very much!
A piece of my data is like the following:
<root>
<outsideData id="123">
<child1 key="124375438"/>
<![CDATA[ <?xml version=1.0 encoding="UTF-8"?><insideData xmlns:xlink="http://www.w3.org/1999/xlink">
<nodeLevel1>
<nodeLevel21>packing</nodeLevel21>
<nodeLevel22 ref="12343-454/560" xlink:href="URN:X-MN:DD%3FM=B888%26SDC=A%26CH=79% .../>
</nodeLevel1>
]]>
</outsideData>
</root>
I want to get the inside CDATA <nodeLevel22> #ref and #xlink which will get DD-FM-B888-26-79
My variables are:
<xsl:for-each select="/root/outsideData">
<xsl:variable name="insideData">
<xsl:value-of select="." disable-output-escaping="yes"/>
</xsl:variable>
<xsl:variable name="Data">
<xsl:value-of
select="normalize-space(substring-after($insideData,'?>'))"
disable-output-escaping="yes"/>
</xsl:variable>
</xsl:foreach>
From the debug I can see that the variable insideData and Data are both value type of document-node(1)
Martin's solution works for me very well :)
But I'm still wondering why the following doesn't work:
<xsl:variable name="insideData">
<xsl:value-of select="." disable-output-escaping="yes"/>
</xsl:variable>
<ref>
<xsl:value-of select="substring-before(substring-after($insideData, '<nodeLevel22 ref'),>/>')"/>
</ref>
Here I got empty <ref/>
If you do <xsl:variable name="varName"><xsl:value-of select="..."/><xsl:variable> then you are creating a temporary document fragment that contains a single text with the string contents of the item(s) selected in the value-of. That does not make sense in most cases, doing <xsl:variable name="varName" select="..."/> is usually sufficient.
As for parsing the contents of the outsideData element with parse-xml, there is indeed not only the escaped XML document inside that element but white space as well, thus if you try to parse the contents as XML you get that error as white space before the XML declaration is not allowed. The whole approach of stuffing the XML into a CDATA section with an element with mixed contents is flawed in my view, if you want to store escaped XML into a CDATA then you should make sure that you use a single element that contains nothing but the CDATA section which then only contains the XML markup with no leading white space.
If you can't change the creation of the input data then you will need to make sure you pass in only that part of the string contents of the element to parse-xml that is a well-formed XML document, so you need some way to strip the white space before the XML declaration doing e.g.
<xsl:for-each select="/root/outsideData">
<xsl:variable name="xml-string" select="replace(., '^\s+', '')"/>
<xsl:variable name="xml-doc" select="parse-xml($xml-string)"/>
<!-- now output data e.g. -->
<xsl:value-of select="$xml-doc//nodeLevel1/nodeLevel22/#ref"/>
...
</xsl:for-each>
Untested but should show the right direction as far as trying to use parse-xml.

In XSLT, is it normal that a variable set to something like name(..) is computed at time of use?

I have a couple of trees in my XML and wanted to access one in terms of a name in the other. Here is is called the tab_name and it is the parent tag of the current node so I use name(..). That gives me the correct value if I test at the same location where I set the variable.
However, the problem I have is that when I reference $tab_name a few lines below (in the <xsl:when> tag) the name(..) is applied to the current context so I get the tag "group" instead of what I would otherwise expect.
<xsl:variable name="tab_name" select="name(..)"/>
<legend>
<xsl:for-each select="/snap/page/body/client/group/*">
<xsl:choose>
<xsl:when test="name(.) = $tab_name"> <!-- $tab_name = 'group' here! -->
...
</xsl:when>
</xsl:choose>
</xsl:for-each>
</legend>
Is that the normal/expected behavior of XSLT 2.0? I was thinking that the variable would be set in its own for-each context (for-each not shown here) and not the new sub-for-each context.
Here are full XSLT and XML documents to reproduce the problem with xmlpatterns (the Qt XML parser).
XSLT (say a.xsl):
<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:snap="snap:snap">
<xsl:template match="snap">
<xsl:for-each select="page/body/client/data_field/*">
Direct name = <xsl:value-of select="name(.)"/> [correct, getting 'dog']
<xsl:for-each select="*">
<xsl:variable name="tab_name" select="name(..)"/>
Parent name = <xsl:value-of select="$tab_name"/> [correct, getting 'dog']
<xsl:message>Message has no side-effects... <xsl:value-of select="$tab_name"/></xsl:message>
<xsl:for-each select="/snap/page/body/client/group">
Inside other for-each tab_name = <xsl:value-of select="$tab_name"/> [incorrect, getting 'client']
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
<!-- vim: ts=2 sw=2
-->
XML (say a.xml):
<!DOCTYPE snap>
<snap>
<page>
<body layout-name="finball">
<client>
<group>
<cat>Jolly</cat>
<dog>Bear</dog>
</group>
<data_field>
<cat>
<div>All about Cats</div>
</cat>
<dog>
<div>All about Dogs</div>
</dog>
</data_field>
</client>
</body>
</page>
</snap>
<!--
vim: ts=2 sw=2 et
-->
Command I use to reproduce the problem:
xmlpatterns a.xsl a.xml
The output is incorrect:
Direct name = cat [correct, getting 'cat']
Parent name = cat [correct, getting 'cat']
Inside other for-each tab_name = client [incorrect, getting 'client']
Direct name = dog [correct, getting 'dog']
Parent name = dog [correct, getting 'dog']
Inside other for-each tab_name = client [incorrect, getting 'client']
(As a detail: I'm using Qt XSTL 2.0 implementation, in case it is not normal, then the Qt implementation is what is broken.)
This is clearly a bug in the Qt XSLT 2.0 processor. At their website they claim that they only partially support XSLT 2.0. However, this behavior of assigning variables has not changed between 1.0 and 2.0.
"In XSLT, is it normal that a variable set to something like name(..) is computed at time of use?"
Yes, it is normal that is computed "at the time it is used", to prevent unnecessary overhead. Most processors (including ours, in most cases), will calculate variables once and only when used. However, they must be calculated in light of their declaration context, which is clearly not what's happening here:
The declaration of the variable defines its focus and context, not the place where the variable is referenced.
When I run your input XML and stylesheet XSLT against Saxon or Exselt (or probably any other processor out there that I haven't tried), it gives the following output (outdented and whitelines removed for clarity):
<?xml version="1.0" encoding="UTF-8"?>
Direct name = cat [correct, getting 'dog']
Parent name = cat [correct, getting 'dog']
Inside other for-each tab_name = cat [incorrect, getting 'client']
Direct name = dog [correct, getting 'dog']
Parent name = dog [correct, getting 'dog']
Inside other for-each tab_name = dog [incorrect, getting 'client']
As you can see, it is either always "dog" or always "cat", as it should be.
I suggest you file a bug against this processor or, considering it is open source and if you have the time, help them out to fix it at the source ;).

how to reassign value to a variable in xslt

Here I am trying to run a single loop that searches entire xml and depending on the various conditions different variable get different values . so that it can be used later for reference.
Sample code :
<xsl:for-each select='root'>
<xsl:choose>
<xsl:when test='first'>
<xsl:variable name='first' select='root/first' />
</xsl:when>
<xsl:when test='second'>
<xsl:variable name='namew' select='root/second' />
</xsl:when>
<xsl:otherwise>
<xsl:variable name='other'>unknown</xsl:variable>
</xsl:otherwise>
</xsl:choose>
I Know it wont work here and i also know the reason (variable scope and constant behavior of variable) , actually i want to know the alternative solution to this problem.
XSLT is a functional language.
Among many things, this means that the value of a variable, once defined, cannot be changed.
If you specify a particular problem that you want to solve, many of us will be able to give you a solution in which variable's values aren't changed.

Resources