using a single namespace declaration for literal result element and element constructor methods - xslt-2.0

Given the XML source
<Content>
</Content>
and transformation:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
office:version="1.0"
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">
<xsl:output indent="yes" encoding="UTF-8"/>
<xsl:template match="Content">
<xsl:element name="office:document">
<xsl:attribute name="office:version">1.2</xsl:attribute>
<xsl:attribute name="office:mimetype">application/vnd.oasis.opendocument.text</xsl:attribute>
<xsl:element name="office:body">
<xsl:element name="office:text">
<xsl:element name="text:p">Hello world.
</xsl:element>
<xsl:element name="text:p">Goodbye world.
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
The result
<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
office:version="1.2"
office:mimetype="application/vnd.oasis.opendocument.text">
<office:body>
<office:text>
<text:p xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">Hello world.
</text:p>
<text:p xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">Goodbye world.
</text:p>
</office:text>
</office:body>
</office:document>
The namespace for paragraph elements is repeated. I want it applied to the root element to avoid this, as is the norm in odf files.
But if I add the namespaces to the root element, the XSL will include redundant namespace declarations, for the spreadsheet and root elements.
If I then remove the namespaces from the stylesheet element, I won't be able to add literal result elements in those namespaces.
I read in Kay's 4th edition reference p473 "Avoiding duplicate namespace declarations is entirely the job of the XSLT serializer.."
But I'm unable to leverage this insight to produce the required result.

For the included sample you get the included result as the elements generated with xsl:element don't have any namespaces in scope that you declare in the stylesheet element, they are only used to create an element in a certain namespace. It is not clear from that sample why you need xsl:element at all and can't simply use literal result elements.
If you really need to construct your root element with xsl:element, you can however construct a namespace node with <xsl:namespace name="text" select="'urn:oasis:names:tc:opendocument:xmlns:text:1.0'"/>. See https://xsltfiddle.liberty-development.net/jyH9rMg for an online example which transforms your input sample with
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
office:version="1.0"
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">
<xsl:output indent="yes" encoding="UTF-8"/>
<xsl:template match="Content">
<xsl:element name="office:document">
<xsl:namespace name="text" select="'urn:oasis:names:tc:opendocument:xmlns:text:1.0'"/>
<xsl:attribute name="office:version">1.2</xsl:attribute>
<xsl:attribute name="office:mimetype">application/vnd.oasis.opendocument.text</xsl:attribute>
<xsl:element name="office:body">
<xsl:element name="office:text">
<xsl:element name="text:p">Hello world.
</xsl:element>
<xsl:element name="text:p">Goodbye world.
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
into
<office:document xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
office:version="1.2"
office:mimetype="application/vnd.oasis.opendocument.text">
<office:body>
<office:text>
<text:p>Hello world.
</text:p>
<text:p>Goodbye world.
</text:p>
</office:text>
</office:body>
</office:document>

Related

creating a tree structure from a delimited string xslt 2.0

I have a input string looks like below
test1->test2->test3
I want to build a tree structure like the below.
-test1
+test2
How can I convert the string to tree structure using xslt 2.0.
The following stylesheet splits the string into a sequence of strings using tokenize() and then recursively calls the "nest" template to create an element for the first item in the sequence and then call the template with the remaining strings to generate the nested elements.
<?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"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output indent="yes"/>
<xsl:template match="/">
<xsl:variable name="delimited-input" select="'test1->test2->test3'"/>
<xsl:call-template name="nest">
<xsl:with-param name="names" select="tokenize($delimited-input, '->')"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="nest" as="element()*">
<xsl:param name="names" as="xs:string*"/>
<xsl:if test="exists($names)">
<xsl:variable name="head" select="$names[position() = 1]"/>
<xsl:element name="{$head}">
<xsl:call-template name="nest">
<xsl:with-param name="names" select="$names[position() > 1]"/>
</xsl:call-template>
</xsl:element>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Produces the following nested element structure:
<test1>
<test2>
<test3/>
</test2>
</test1>
Assuming that you want to produce HTML, adjust to generate <div> or whatever specific elements necessary.

XSLT 2 - pick item from tokenize()'d list by index

My environment is SAXON (last nights build) using XSLT 2.0. My real problem is that the XML document specification is sub-optimal, and in a way, my problem relates to fixing/working around that design issue.
I have a node type (<weaponmodesdata>) where all the direct children are |-separated string lists of 1-or-many elements (each child of the same <weaponmodesdata> will have the same length). I need to go over the various modes represented and "unspin" them out to separate item lists (in plain text), rather than having them all smooshed together.
Unfortunately right now I'm getting a really stubborn
XPTY0020: Required item type of the context item for the child axis is node(); supplied
value has item type xs:string
error on the lines where I pass the node that needs to be split up into my little template.
Currently I have
<xsl:template match="trait" mode="attack">
<xsl:for-each select="tokenize(weaponmodesdata/mode, '\|')">
<xsl:variable name="count" select="position()"/>
<xsl:value-of select="name"/><xsl:text> - </xsl:text>
<xsl:call-template name="split_weaponmode">
<xsl:with-param name="source" select="weaponmodesdata/damage"/>
<xsl:with-param name="item" select="$count"/>
</xsl:call-template>
<xsl:text> </xsl:text>
<xsl:call-template name="split_weaponmode">
<xsl:with-param name="source" select="weaponmodesdata/damtype"/>
<xsl:with-param name="item" select="$count"/>
</xsl:call-template>
<!-- more will go here eventually -->
<xsl:text>.
</xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template name="split_weaponmode">
<xsl:param name="source"/>
<xsl:param name="item"/>
<xsl:variable name="parts" select="tokenize($source, '\|')"/>
<xsl:for-each select="$parts">
<xsl:if test="position() = $item">
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
An example XML subtree relating to my issue:
<character>
<trait id="1">
<name>Spear</name>
<weaponmodesdata>
<mode>1H Thrust|2H Thrust|Thrown</mode>
<damage>thr+2|thr+3|thr+3</damage>
<damtype>imp|imp|imp</damtype>
</weaponmodesdata>
</trait>
<trait id="2">
<name>Broadsword</name>
<weaponmodesdata>
<mode>1H Thrust|1H Swing</mode>
<damage>thr+1|sw+2</damage>
<damtype>imp|cut</damtype>
</weaponmodesdata>
</trait>
</character>
Example desired output:
Spear - 1H Thrust; thr+2 imp.
Spear - 2H Thrust; thr+3 imp.
Spear - Thrown; thr+3 imp.
Broadsword - 1H Thrust; thr+1 imp.
Broadsword - 1H Swing; sw+2 cut.
One issue (that one causing the error message) with your code is that your for-each operates on a sequence of string value (i.e. inside the for-each body the context item is a string value), yet you have relative XPath expressions like weaponmodesdata/damage that require a context node to makes sense. So you would need to use a variable outside of the for-each to store your context node.
But I think you can simplify your code to
<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 method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="trait">
<xsl:variable name="this" select="."/>
<xsl:variable name="count" select="count(tokenize(weaponmodesdata/*[1], '\|'))"/>
<xsl:for-each-group select="weaponmodesdata/*/tokenize(., '\|')" group-by="position() mod $count">
<xsl:value-of select="$this/name"/>
<xsl:text> - </xsl:text>
<xsl:value-of select="current-group()"/>
<xsl:text>.
</xsl:text>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
If you want to stick with your approach of calling templates then make sure you store the context node of the template using e.g. <xsl:variable name="this" select="."/> so that you can access it inside of the for-each iterating over a string item.

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.

xslt: keeping namespace declaration on root when root element is not known in advance

I have xml documents that follow a schema where most of the defined elements are allowed to be the root of a valid instance. I also have several xslt's v2.0 which translate it in various ways (put it into a normal form, a compact form, a different dialect, ...) These xslt's are all based on an identity transform with templates added to make the desired modification. The problem is that there is a proliferation of namespace attributes because there are some elements that come from outside the default namespace.
I have tried the recommended procedures for inserting the namespace on the root element, but I can't seem to get it right. The issues are:
1. the transformation may change the name, and sometimes the content of the root element, so I still need the templates for each of the global elements, and since I don't know which one will be root, I can't just insert namespace elements where needed (I don't know where they will be needed for a particular document.
2. I thought about implementing this as multi-pass, or simply an independent xslt, since I want the same result for several different xslts. In this case, what I would need is an identity transform that takes all the namespaces and prefixes from all elements in the document, and inserts them into the root. This would, I hope, automatically remove the namespace attributes from the children? However, I tried the following
<?xml version="1.0" ?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template name="start" match="/">
<xsl:copy>
<xsl:for-each select="*">
<xsl:copy>
<xsl:for-each select="descendant::*">
<xsl:call-template name="add-ns">
<xsl:with-param name="ns-namespace">
<xsl:value-of select="namespace-uri()"/>
</xsl:with-param>
<xsl:with-param name="ns-prefix">
<xsl:value-of
select=" prefix-from-QName( QName(namespace-uri(),name()))"/>
</xsl:with-param>
</xsl:call-template>
</xsl:for-each>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template name="add-ns">
<xsl:param name="ns-prefix" select="'x'"/>
<xsl:param name="ns-namespace" select="'someNamespace'"/>
<xsl:namespace name="{$ns-prefix}" select="$ns-namespace"/>
</xsl:template>
<xsl:template match="node()|#* ">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
And this works for all prefixes that appear on elements, but it doesn't catch the prefixes of attributes. Here is a test document:
<RuleML xmlns="http://www.ruleml.org/0.91/xsd">
<Assert textiri="xy>z">
<Importation xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="abc"
textiri="urn:common-logic:demo1"
xlink:href="http://common-logic.org/x>cl/demos.xml"/>
<a:anything xmlns:a="http://anything.org"
xmlns:xlink="http://www.w3.org/1999/xlink"/>
</Assert>
</RuleML>
I want it to produce:
<RuleML xmlns="http://www.ruleml.org/0.91/xsd" xmlns:a="http://anything.org" xmlns:xlink="http://www.w3.org/1999/xlink" >
<Assert textiri="xy>z">
<Importation xml:id="abc"
textiri="urn:common-logic:demo1"
xlink:href="http://common-logic.org/x>cl/demos.xml"/>
<a:anything/>
</Assert>
</RuleML>
but instead I get
<RuleML xmlns="http://www.ruleml.org/0.91/xsd" xmlns:a="http://anything.org">
<Assert textiri="xy>z">
<Importation xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="abc"
textiri="urn:common-logic:demo1"
xlink:href="http://common-logic.org/x>cl/demos.xml"/>
<a:anything xmlns:xlink="http://www.w3.org/1999/xlink"/>
</Assert>
</RuleML>
Tara
Does the following do what you want?
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:template match="#* | node()">
<xsl:copy copy-namespaces="no">
<xsl:apply-templates select="#* , node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:copy>
<xsl:copy-of select="descendant::*/namespace::*"/>
<xsl:apply-templates select="#* , node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
With Saxon 9.3 it seems to do the job on the sample you posted.
I am however not sure what you want to do if there are several elements in different default namespaces or several elements in different namespaces but using the same prefix. For instance with
<root xmlns="http://example.com/ns1">
<foo xmlns="http://example.com/ns2">
<pf:bar xmlns:pf="http://example.com/ns3">
<pf:foobar xmlns:pf="http://example.com/ns4"/>
</pf:bar>
</foo>
</root>
Saxon simply reports the error
Error at xsl:copy-of on line 15 of test2011061801Xsl2.xsl:
XTDE0430: Cannot create two namespace nodes with the same prefix mapped to different URIs
(prefix="", URI=http://example.com/ns2, URI=http://example.com/ns1)
in built-in template rule
[edit]
If you don't want an error to be reported you could try to implement a strategy to pull up namespace nodes as far up as possible but to avoid any collisions. That can be done with for-each-group, as in the following sample XSLT 2.0:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:template match="#* | text() | processing-instruction() | comment()">
<xsl:copy/>
</xsl:template>
<xsl:template match="*">
<xsl:copy copy-namespaces="no">
<xsl:for-each-group select="descendant-or-self::*/namespace::*" group-by="local-name()">
<xsl:copy-of select="."/>
</xsl:for-each-group>
<xsl:apply-templates select="#* , node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
With the input being
<root xmlns="http://example.com/ns1">
<foo xmlns="http://example.com/ns2">
<pf:bar xmlns:pf="http://example.com/ns3">
<pf:foobar xmlns:pf="http://example.com/ns4"/>
</pf:bar>
</foo>
</root>
Saxon 9.3 outputs
<?xml version="1.0" encoding="UTF-8"?><root xmlns="http://example.com/ns1" xmlns:pf="http://example.com/ns3">
<foo xmlns="http://example.com/ns2">
<pf:bar>
<pf:foobar xmlns:pf="http://example.com/ns4"/>
</pf:bar>
</foo>
</root>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="*:RuleML">
<xsl:copy>
<xsl:for-each select="descendant::node()">
<xsl:choose>
<xsl:when test="self::text()"/>
<xsl:otherwise>
<xsl:for-each select="namespace::node()">
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:apply-templates select="(node() | #*) except namespace::node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="(node() | #*) except namespace::node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Complex XSL Transformation

I am still a beginner with XSLT but I am having a difficult task in hand.
I have a non-xml file which needs to be transformed. The format of the file is a s follows:
type1
type1line1
type1line2
type1line3
type2
type2line1
type2line2
type3
type3line1
type3line2
types (type1, type2, ...) are specified using certain codes which don't have a specific order. Each type has multiple line underneath.
So, I need to transform this file but the problem is that for each type I have to do a different transformation for each of it's underlying lines.
Now, I can read the string line by line and determine that a new type has begun but I don't know how to set a flag (indicating the type) to use it in the underlying lines.
Here is what I have right now:
<?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="2.0">
<xsl:param name="testString" as="xs:string">
type1
line1
line2
type1
line1
</xsl:param>
<xsl:template match="/">
<xsl:call-template name="main">
<xsl:with-param name="testString" select="$testString"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="main">
<xsl:param name="testString"/>
<xsl:variable name="iniFile" select="$testString"/>
<config>
<xsl:analyze-string select="$iniFile" regex="\n">
<xsl:non-matching-substring>
<item>
<xsl:choose>
<xsl:when test="starts-with(., 'type1')">
<!-- do a specific transformation-->
</xsl:when>
<xsl:when test="starts-with(., 'type2')">
<!-- do another transformation-->
</xsl:when>
</xsl:choose>
</item>
</xsl:non-matching-substring>
</xsl:analyze-string>
</config>
</xsl:template>
</xsl:stylesheet>
Any idea about how to solve the problem.
I think XSLT 2.1 will allow you to use its powerful stuff like for-each-group on sequences of atomic values like strings but with XSLT 2.0 you have such powerful features only for sequences of nodes so my first step when using XSLT 2.0 with plain string data I want to process/group is to create elements. So you could tokenize your data, wrap each token into some element and then use for-each-group group-starting-with to process each group starting with some pattern like '^type[0-9]+$'.
You haven't really told us what you want to with the data once you have identified a group so take the following as an example you could adapt:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="input" as="xs:string">type1
type1line1
type1line2
type1line3
type2
type2line1
type2line2
type3
type3line1
type3line2</xsl:param>
<xsl:template name="main">
<xsl:variable name="lines" as="element(item)*">
<xsl:for-each select="tokenize($input, '\n')">
<item><xsl:value-of select="."/></item>
</xsl:for-each>
</xsl:variable>
<xsl:for-each-group select="$lines" group-starting-with="item[matches(., '^type[0-9]+$')]">
<xsl:choose>
<xsl:when test=". = 'type1'">
<xsl:apply-templates select="current-group() except ." mode="m1"/>
</xsl:when>
<xsl:when test=". = 'type2'">
<xsl:apply-templates select="current-group() except ." mode="m2"/>
</xsl:when>
<xsl:when test=". = 'type3'">
<xsl:apply-templates select="current-group() except ." mode="m3"/>
</xsl:when>
</xsl:choose>
</xsl:for-each-group>
</xsl:template>
<xsl:template match="item" mode="m1">
<foo>
<xsl:value-of select="."/>
</foo>
</xsl:template>
<xsl:template match="item" mode="m2">
<bar>
<xsl:value-of select="."/>
</bar>
</xsl:template>
<xsl:template match="item" mode="m3">
<baz>
<xsl:value-of select="."/>
</baz>
</xsl:template>
</xsl:stylesheet>
When applied with Saxon 9 (command line options -it:main -xsl:sheet.xsl) the result is
<foo>type1line1</foo>
<foo>type1line2</foo>
<foo>type1line3</foo>
<bar>type2line1</bar>
<bar>type2line2</bar>
<baz>type3line1</baz>
<baz>type3line2</baz>

Resources