Warning: unrecognized element xsl:variable online running saxon but not in Oxygen, why? - saxon

I have a schematron-file looking like this (as simply as possible)
<sch:pattern id="TDOP_0400">
<sch:rule context="//tekst:Kop">
<xsl:variable name="CONDITION">
<xsl:value-of select="tekst:Label and tekst:Opschrift and tekst:Nummer"/>
</xsl:variable>
</sch:rule>
</sch:pattern>
When I run it in Oxygen, it runs fine, but when I run it from the commandline I get an error.
This is my commandline command:
$ java -cp ../saxon9.9.1.5/saxon9he.jar net.sf.saxon.Transform -t -s:tpod0400.sch -xsl:../saxon9.9.1.5/iso_svrl_for_xslt2.xsl -o:tpod0400.xsl
This is the error message, I get it for every xsl:variable-line:
Warning: unrecognized element xsl:variable

Add "allow-foreign=true" to the command:
$ java -cp ../saxon9.9.1.5/saxon9he.jar net.sf.saxon.Transform -t -s:tpod0400.sch -xsl:../saxon9.9.1.5/iso_svrl_for_xslt2.xsl -o:tpod0400.xsl allow-foreign=true
"allow-foreign" is a parameter in iso_svrl_for_xslt2.xsl, which is documented as follows in the stylesheet:
Pass non-Schematron elements and rich markup to the generated
stylesheet
xsl:variable is a non-Schematron element and is left out unless allow-foreign=true.

Best and most safe practice is to separate sch (schematron) from xslt code. Keep the sch-code inside the rule, and if you need xslt-functionality, break out with a function call to xslt-code, then return the result of the funtion back to the sch-code, and go on with sch-processing.
So short: Never mix up sch and xslt in one programming context, and you will always be safe.
I have an example, never mind what it does or what it is for, it is here to demonstrate how to break out an sch-context to invoke some xslt-code, and then return back to the sch-context:
<sch:pattern id="TPOD_0520">
<sch:rule context="//tekst:Hoofdstuk/tekst:Titel">
<sch:let name="APPLICABLE"
value="$SOORT_REGELING = $OP or $SOORT_REGELING = $OV or $SOORT_REGELING = $WV"/>
<sch:let name="hoofdstuk" value="string(../tekst:Kop/tekst:Nummer)"/>
<sch:let name="titel" value="string(tekst:Titel/tekst:Kop/tekst:Nummer)"/>
<!-- Below is the break-out to XSLT, and the value coming from the function is used in the sch-code -->
<sch:let name="volgorde" value="foo:volgordeTPOD_0520($titel, .)"/>
<sch:let name="CONDITION" value="string-length($volgorde) = 0"/>
<sch:assert test="($APPLICABLE and $CONDITION) or not($APPLICABLE)">
TPOD_0520: Als tussen Hoofdstuk en Afdeling Titel voorkomt dan moet de nummering van Afdelingen beginnen met het samengestelde nummer van de Titel waarin de Afdeling voorkomt, gevolgd door een punt. (betreft hoofdstukken, titels, afdelingen): <xsl:value-of select="$hoofdstuk"/>: <sch:value-of select="$titel"/>: <sch:value-of select="substring($volgorde,1,string-length($volgorde)-2)"/></sch:assert>
</sch:rule>
</sch:pattern>
<xsl:function name="foo:volgordeTPOD_0520">
<xsl:param name="titel" as="xs:string"/>
<xsl:param name="context" as="node()"/>
<xsl:variable name="volgorde">
<xsl:for-each select="$context/tekst:Afdeling">
<xsl:if test="not(string(tekst:Kop/tekst:Nummer)=concat($titel, '.', string(position())))">
<xsl:value-of select="concat(string(tekst:Kop/tekst:Nummer),', ')"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="$volgorde"/>
</xsl:function>

Related

Return a value type of element from a function in XSLT

This is my XML
<report>
<format-inputs>
<narrative-entity-ids>
<entity id="28495795" type-cdf-meaning="DIAGNOSES"/>
<entity id="28495741" type-cdf-meaning="DIAGNOSES"/>
<entity id="28495471" type-cdf-meaning="DIAGNOSES"/>
</narrative-entity-ids>
</format-inputs>
</report>
I am creating a function in commonFunction.xslt
<xsl:function name="cdocfx:createEntityIdList" >
<xsl:param name="formatInputsNodes"/>
<xsl:if test="fn:exists(n:report/n:format-inputs)"
<xsl:variable name="entityIdList" as="element()*">
<xsl:for-each select="$formatInputsNodes/n:narrative-entity-ids/n:entity">
<Item><xsl:value-of select="#id"/></Item>
</xsl:for-each>
</xsl:variable>
</xsl:if>
<xsl:copy-of select="$entityIdList"/>
</xsl:function>
I am calling this function in other xslt file where commonFunction.xslt was included
<xsl:variable name="entityIdList" select="cdocfx:createEntityIdList(n:report/n:format-inputs)"/>
</xsl:variable>
My question is variable entityIdList should be value type of element but it is having the document-node type how can i achieve this ??
Please provide minimal but complete samples of XML input, XSLT you have, output you want and the one you get together with any exact error messages you have encountered.
I am currently not sure I understand what you want to achieve, if you construct a variable of type element()* you seem to want to construct a sequence of element nodes. Any xsl:value-of however will only output the string values of the selected items in a text node so it is not clear why you first construct elements if you only want to output string values. If you construct nodes and want to output them use xsl:copy-of or xsl:sequence, not xsl:value-of.
To show two examples of writing a function that returns a sequence of elements (i.e. whose result is of type element()*) I have set up https://xsltfiddle.liberty-development.net/3NzcBtE which has two functions
<xsl:function name="mf:ex1">
<xsl:param name="input" as="element()*"/>
<xsl:for-each select="$input">
<item>{ #id }</item>
</xsl:for-each>
</xsl:function>
<xsl:function name="mf:ex2">
<xsl:param name="input" as="element()*"/>
<xsl:variable name="elements" as="element()*">
<xsl:for-each select="$input">
<item>{ #id }</item>
</xsl:for-each>
</xsl:variable>
<xsl:sequence select="$elements"/>
</xsl:function>
the first simply directly constructs some result elements in the function body, that way the result is a sequence of element nodes. The second function uses your approach of constructing a sequence of elements nodes in a variable, the proper way to return that variable value then from the function is to use xsl:sequence.
It is not clear at which position of the posted code you think are dealing with a document-node() node.
Note also that
<xsl:choose>
<xsl:when test="fn:exists($formatInputsNodes/n:narrative-entity-ids)">
<xsl:for-each select="$formatInputsNodes/n:narrative-entity-ids/n:entity">
<Item><xsl:value-of select="#id"/></Item>
</xsl:for-each>
</xsl:when>
</xsl:choose>
can be simplified to
<xsl:for-each select="$formatInputsNodes/n:narrative-entity-ids/n:entity">
<Item><xsl:value-of select="#id"/></Item>
</xsl:for-each>
As you have now presented an XML input that is at least well-formed and some XSLT snippets (that are unfortunately not well-formed and seem to use a namespace although the XML input shown doesn't use one) here is an attempt to morph that into a working sample
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:cdocfx="http://example.com/cdox-functions"
exclude-result-prefixes="#all"
version="3.0">
<xsl:function name="cdocfx:createEntityIdList" >
<xsl:param name="formatInputsNodes"/>
<xsl:variable name="entityIdList" as="element()*">
<xsl:for-each select="$formatInputsNodes/narrative-entity-ids/entity">
<Item><xsl:value-of select="#id"/></Item>
</xsl:for-each>
</xsl:variable>
<xsl:copy-of select="$entityIdList"/>
</xsl:function>
<xsl:variable name="entityIdList" select="cdocfx:createEntityIdList(report/format-inputs)"/>
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:value-of select="$entityIdList instance of element()*, $entityIdList" separator=", "/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/pPqsHTW/1
Output there for the check $entityIdList instance of element()* is true so I am not sure why you say you have a document node.

pattern not matching though declared

I've the below XML.
<root>
<para>
<label>5.</label> In essence, the Court in <star.page>19</star.page>
</para>
<para>
<label><star.page>21</star.page> 13.</label> Frankly, I cannot see how
one can escape
</para>
</root>
and using the below XSLT.
<xsl:template match="para">
<xsl:apply-templates select="./node()[1][self::star.page]|./label/node()[1][self::star.page]" mode="first"/>
</xsl:template>
<xsl:template match="star.page" mode="first">
<xsl:if test="preceding::star.page">
<xsl:processing-instruction name="pb">
<xsl:text>label='</xsl:text>
<xsl:value-of select="."/>
<xsl:text>'</xsl:text>
<xsl:text>?</xsl:text>
</xsl:processing-instruction>
<a name="{concat('pg_',.)}"/>
</xsl:if>
</xsl:template>
here when i try to run this code, the first para star.page is getting caught, but the second star.page, i.e. <para><label><star.page>21</star.page> 13.</label>... is not getting caught. please let me know where am i going wrong. here i'm taking [1], since i want to catch the first occurance.
Thanks
I just tried your code on xmlplayground, both the star.page elements reach the template but the if clause is preventing the first from reaching the output.

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 ;).

Saxon-ce dynamic xpath evaluation

I am trying to evaluate a dynamic xpath using ixsl:eval() in Saxon-ce with xslt 2.0 but does not seem to be working. Here is the illustrative XML
<things>
<thing>
<name>widget one</name>
<number>10</number>
<type>metal</type>
<subtypes>
<subtype>red<subtype>
</subtypes>
</thing>
<thing>
<name>widget two</name>
<number>11</number>
<type>wood</type>
<subtypes>
<subtype>red</subtype>
<subtype>blue</subtype>
</subtypes>
</thing>
</things>
and a piece of the xsl 2.0 style sheet I am trying to eval (the various parameters are passed by another part of the larger xsl stylesheet)
<template name="display" match="things">
<xsl:param name="num"/>
<xsl:param name="type" as="xs:string"/>
<xsl:param name="subtype" as="xs:string"/>
<xsl:variable name="xpathExp" as="xs:string">
<xsl:text>things/thing</xsl:text>
<xsl:if test="not($num = 'all')>
<xsl:copy-of select="concat('[number=',$num,']')"/>
</xsl:if>
<xsl:if test="not($type = 'all')>
<xsl:copy-of select="concat('[type=''',$type,''']')"/>
</xsl:if>
<xsl:if test="note($subtype = 'all')>
<xsl:copy-of select="concat('[subtype/subtypes=''',$subtype,''']')/>
</xsl:if>
</xsl:variable>
<xsl:result-document href="#display" method="ixsl:replace-content">
<xsl:for-each select="ixsl:eval($xpathExp)">
<xsl:sort select="name"/>
</xsl:for-each>
</template>
When I replace the eval statement with a explicit xpath statement the code works, so for some reason the eval on $xpathExp is not working. Ideas?
* Edit *
Here is a better XML example:
<things>
<thing>
<name>widget one</name>
<number>10</number>
<type>metal</type>
<subtypes>
<subtype>red<subtype>
</subtypes>
</thing>
<thing>
<name>widget two</name>
<number>11</number>
<type>wood</type>
<subtypes>
<subtype>red</subtype>
<subtype>blue</subtype>
</subtypes>
</thing>
<thing>
<name>widget three</name>
<number>11</number>
<type>metal</type>
<subtypes>
<subtype>blue</subtype>
</subtypes>
</thing>
</things>
The user can select values via dropboxes for number, type and subtype. Depending on the user's selection, a list of thing names is displayed. So for example, if the user selects a number of 11 and the subtype of red it would just display widget two. If they select instead a subtype of blue it would display the name of widgets two and three.
So the base xpath filter is things/thing. If the user selects a number value I want to append [number=$num] to the xpath expression so ti would be things/thing[number=$num]. If they select more than one item, lets say number and type, [number=$num][type=$type] would be appended to the base xpath and I would have things/thing[number=$num][type=$type].
Basically what I trying to avoid is having to individually code all possible permutations and combinations of possible user selections.
Does that help?
The ixsl:eval function is for dynamic evaluation of JavaScript, not XPath. From the Saxon-CE documentation:
...Executes Javascript code, supplied as a string. The supplied script is
injected into the DOM as a function which is evaluated immediately to
return a result.
For the code sample you provide, dynamic evaluation is not required. Only certain classes of XSLT application actually require dynamic XPath evaluation.
In the few cases where dynamic XPath evaluation is required, Saxon-CE has no built-in capability, so instead of just building an XPath dynamically, you would need to generate a simple 'wrapper' XSLT and execute this using the Saxon-CE JavaSCript API. This technique is used in the PathEnq online XPath 2.0 evaluator.
The following code achieves something similar to your code using a single 'static' XPath expression - in this example, a further template is applied to the required 'thing' element to create usable HTML:
rendered code (syntax-highlighted and formatted)
source code:
<xsl:transform
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ixsl="http://saxonica.com/ns/interactiveXSLT"
xmlns:js="http://saxonica.com/ns/globalJS"
xmlns:prop="http://saxonica.com/ns/html-property"
xmlns:style="http://saxonica.com/ns/html-style-property"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs prop"
extension-element-prefixes="ixsl"
version="2.0"
>
<xsl:template name="main" match="/">
<xsl:call-template name="display"/>
</xsl:template>
<xsl:template name="display">
<xsl:param name="num" as="xs:string" select="'none'"/>
<xsl:param name="type" as="xs:string" select="'all'"/>
<xsl:param name="subtype" as="xs:string" select="'red'"/>
<xsl:variable name="thingsEl" select="things" as="element(things)"/>
<xsl:variable name="widget" as="element()+"
select="if ($num eq 'all')
then $thingsEl/thing[number = $num]
else if ($type ne 'all')
then $thingsEl/thing[type = $type]
else $thingsEl/thing[subtypes/subtype = $subtype]"/>
<xsl:result-document href="#display" method="append-content">
<xsl:apply-templates select="$widget">
<xsl:sort select="name"/>
</xsl:apply-templates>
</xsl:result-document>
</xsl:template>
<xsl:template match="thing">
<ol>
<li>name: <xsl:value-of select="name"/></li>
<li>number: <xsl:value-of select="number"/></li>
<li>type: <xsl:value-of select="type"/></li>
<li>subtypes:
<ol>
<xsl:apply-templates select="subtypes/subtype"/>
</ol>
</li>
</ol>
</xsl:template>
<xsl:template match="subtype">
<li><xsl:value-of select="."/></li>
</xsl:template>
</xsl:transform>
The HTML output (outerHTML view of the 'display' div element)
<div id="display">
<ol>
<li>name: widget one</li>
<li>number: 10</li>
<li>type: metal</li>
<li>subtypes:
<ol>
<li>red</li>
</ol></li>
</ol>
<ol>
<li>name: widget two</li>
<li>number: 11</li>
<li>type: wood</li>
<li>subtypes:
<ol>
<li>red</li>
<li>blue</li>
</ol></li>
</ol>
</div>

Eliminate line-breaks with XSLT 2.0 analyze-string

I use the XSLT 2.0 element analyze-string in a stylesheet that transforms XML to HTML; specifically, I use it to convert string encoding for subscripts in chemical formulae to HTML subscripts. Therefore, the result is a string, to go in a p or td element, with embedded mark-up.
The transformation is supposed to produce output like H2O but in fact inserts a line-break in the HTML:
H
<sub>2</sub>O
and this break is (correctly) interpreted by the browser as a space:
H
2O
which is ugly.
Is there a way to remove the line-break? I've tried putting the whole analyze-string element on one line and that doesn't work.
The input would be something like
<OrdinaryStructralFormula>H$_2$O</OrdinaryStructuralFormula>
for a simple case and
<OrdinaryStructralFormula>C$_2$OH$_5$$^-</OrdinaryStructuralFormula>
for a more-complicated one. Note that the subscript pattern can match multiple times in the general case and can be either in the middle or at the end of the string. The pattern also has to match and eliminate any notation for charge: the $^- bit at the end of the second example.
The XSLT processor is Saxon 9.4 and the XSLT template follows.
<xsl:template name="formula">
<xsl:param name="formula"/>
<xsl:if test="$formula">
<xsl:variable name="f" select="translate($formula, '$', '')"/>
<xsl:analyze-string select="$f" regex="(_)(\d+)|(\^)\d*\+|(\^)\d*\-">
<xsl:matching-substring>
<xsl:if test="regex-group(1)='_'">
<sub><xsl:value-of select="regex-group(2)"/></sub>
</xsl:if>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:if>
</xsl:template>
I cannot reproduce the reported result.
This transformation (which is what you should have given us, but you only provided a template):
<xsl:stylesheet version="2.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="formula">
<xsl:with-param name="formula" select="/*"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="formula">
<xsl:param name="formula"/>
<xsl:if test="$formula">
<xsl:variable name="f" select="translate($formula, '$', '')"/>
<xsl:analyze-string select="$f" regex="(_)(\d+)|(\^)\d*\+|(\^)\d*\-">
<xsl:matching-substring>
<xsl:if test="regex-group(1)='_'">
<sub><xsl:value-of select="regex-group(2)"/></sub>
</xsl:if>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on the following XML document with Saxon 9.1.05:
<formula>H$_2$O</formula>
produces the wanted, correct result:
H<sub>2</sub>O
When the same transformation is applied on the second XML document:
<OrdinaryStructuralFormula>C$_2$OH$_5$$^-</OrdinaryStructuralFormula>
Again the wanted correct result is produced:
C<sub>2</sub>OH<sub>5</sub>
Do note: I ran the same transformations with two other XSLT 2.0 processors: XQSharp (XMLPrime) and AltovaXML (XML-SPY) and got exactly the same, correct results.

Resources