XML convert Node into String using XSLT 2.0 - xslt

I've searched for renaming an XML node to a string. I have found examples of strings within XML to Nodes but not the other way.
Is it possible to do the following
<par>
<run> Some text <break/> </run>
</par>
<par>
<run> some text with no carraige return</run>
</par>
To:
<par>
<run> Some text
</run>
</par>
Many thanks in advance for any replies.
Dono

Certainly that's possible. Just use an identity transform and handle <break> specially. Instead of copying it using <xsl:copy>, output any text you like:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="break">
<xsl:value-of select="'
'"/>
</xsl:template>
</xsl:stylesheet>
I you want the literal output
you can use disable-output-escaping, like
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="break">
<xsl:value-of select="'&#10;'" disable-output-escaping="yes"/>
</xsl:template>
</xsl:stylesheet>
However, this is an optional feature and not guaranteed to be supported by any XSLT processor.

Related

Identity transformation XSLT

I have an incoming XML document. I just need to modify value of one element example <ID> element in this below incoming XML document. I basically need to check for element called <ID> if the value is without any hyphen it will take as it is and if the value contains hyphen(-) then i need to take the value before hyphen (-) ex- 4314141
Incoming XML document:
<Message>
<ID>4314141-324234</ID>
<EMAIL>abc</EMAIL>
</Message>
I am using this below XSL to do do this but it is not working as expected.
XSL:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dp="http://www.datapower.com/extensions"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
extension-element-prefixes="dp"
exclude-result-prefixes="dp" >
<xsl:variable name="uuid" select="dp:variable('var://context/txn/uuid')" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/ID">
<xsl:copy>
<ID><xsl:value-of select="substring-before($ID, ' -')" /></ID>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="ID"/>
</xsl:stylesheet>
Let me know how i can do this.
without any hyphen it will take as it is
This will do your identity-copy template.
if the value contains hyphen(-) then i need to take the value before hyphen (-)
<xsl:template match="ID[contains(., '-')]">
<xsl:copy>
<xsl:value-of select="substring-before(., '-')" />
</xsl:copy>
</xsl:template>
Friendly advice: Please be carefull with / in your matching patterns.
Just use this template overriding the identity rule:
<xsl:template match="ID/text()[contains(., '-')]">
<xsl:value-of select="substring-before(., '-')"/>
</xsl:template>
Here is the complete transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ID/text()[contains(., '-')]">
<xsl:value-of select="substring-before(., '-')"/>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<Message>
<ID>4314141-324234</ID>
<EMAIL>abc</EMAIL>
</Message>
the wanted, correct result is produced:
<Message>
<ID>4314141</ID>
<EMAIL>abc</EMAIL>
</Message>

Change the value of a tag only if another tag has an specific value

I'm using this xsl to change two tags of some xml
xsl
xsltproc - "filename" << EOF
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="no"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root/attr1/text()">
<xsl:text>new-text</xsl:text>
</xsl:template>
<xsl:template match="root/group1/attr1/text()">
<xsl:text>another-new-text</xsl:text>
</xsl:template>
</xsl:stylesheet>
EOF
xml
<root>
<attr1>someold</attr1>
<group1>
<attr1>anotherold</attr1>
</group1>
<attr2>0</attr2>
</root>
output
<root>
<attr1>new-text</attr1>
<group1>
<attr1>another-new-text</attr1>
</group1>
<attr2>0</attr2>
</root>
This xsl works great for my needs but now I need to validate attr2 before the transformation. If attr2 is 0 I need to change, otherwise I should leave the old value.
I have hundreds of xml to convert, each one with hundreds of lines, because of this I'm looking for an automatic way to validate. I tried xsl:if but couldn't figure out where to place the tag and how to build the test attribute.
How to change the value of a tag only if another tag has an specific value? Other improvements on the xsl are also welcome.
You can add conditions in match patterns, e.g. <xsl:template match="root[attr2 = 0]/attr1/text()">...</xsl:match> and/or <xsl:template match="root[attr2 = 0]/group1/attr1/text()">.
you can take the attr2 as variable and use the variable to validate your conditions.....
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="no"/>
<xsl:variable name="attr2" select="root/attr2 "/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root/attr1/text()">
<xsl:value-of select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:text>new-text</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="root/group1/attr1/text()">
<xsl:text>another-new-text</xsl:text>
</xsl:template>

regex error in XSL

Regex should verify such words a23-abcefghijk
<xsl:variable name="myRegex">
<xsl:value-of select="a\\d{2}\\-[a-zA-Z]+" />
</xsl:variable>
but I am getting syntax error I tried escape characters but didnt find any solution yet
The value of your variable needs to be a string, so you need to quote it inside the select...
<xsl:variable name="myRegex" select="'a\d{2}-[a-zA-Z]+'"/>
As it is now, the processor is trying to evaluate your select as an XPath expression.
Also note that if you're using XSLT 1.0, you're going to have to use an extension function (and a processor that supports it).
Here's a 2.0 example...
XML Input
<doc>
<test>a23-abcefghijk</test>
<test>qwerty</test>
</doc>
XSLT 2.0
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="myRegex" select="'a\d{2}-[a-zA-Z]+'"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="test">
<test matches="{if (matches(.,$myRegex)) then 'yes' else 'no'}">
<xsl:value-of select="."/>
</test>
</xsl:template>
</xsl:stylesheet>
XML Output
<doc>
<test matches="yes">a23-abcefghijk</test>
<test matches="no">qwerty</test>
</doc>
<xsl:variable name="myRegex">
<xsl:value-of select="regExp:match('This is a test string', '(a\d{2}\-[a-zA-Z]+)', 'g'" />
</xsl:variable>

XSLT- Creating Parent XML element

I've fairly recently started learning XSLT after getting to grips with XML and XPath; I'm trying to complete a practice exercise; I think I'm nearly there but I've ran into a problem. So I have the following XML document:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<a>
<b></b>
<b></b>
<b></b>
<b></b>
</a>
</root>
And I'd like to surround the elements with a pair of parent elements (to output the following):
<?xml version="1.0" encoding="UTF-8"?>
<root>
<a>
<b-group>
<b></b>
<b></b>
<b></b>
<b></b>
<b-group>
</a>
Here is my XSLT:
<?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:template match="root">
<xsl:element name="root">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="a">
<xsl:element name="a">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="b">
<xsl:element name="b-group">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="b">
<xsl:element name="b">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Despite making several other attempts, I'm having difficulty creating the pair of elements that surround the elements; could anyone point me in the right direction of how to do this please?
You need to do this in the template matching a, for example:
<xsl:template match="a">
<a>
<b-group>
<xsl:apply-templates select="b"/>
</b-group>
</a>
</xsl:template>
Additional notes:
Use a literal result element to create an element whose name is known, instead of xsl:element.
Most of your templates do the same thing: create an element with the same name as the one being matched, and apply templates to its children. Thus they could be consolidated into one. A template like this is known as the identity transform template and it is commonly used when most of the document needs to preserved as is, with only a few modifications. This would reduce your entire stylesheet to just:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="a">
<a>
<b-group>
<xsl:apply-templates select="b"/>
</b-group>
</a>
</xsl:template>
</xsl:stylesheet>

XSLT identity transform produces wrong namespace

I have a custom XML schema that is evolving: elements are added, others deleted, and the namespace changes to reflect the new version (e.g., from "http://foo/1.0" to "http://foo/1.1"). I want to write an XSLT that converts XML documents from the old schema to the new one. My first attempt works, but it's verbose and unscalable, and I need help refining it.
Here's a sample document for the 1.0 schema:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo:rootElement xmlns:foo="http://foo/1.0">
<obsolete>something</obsolete>
<stuff>
<alpha>a</alpha>
<beta>b</beta>
</stuff>
</foo:rootElement>
In the 1.1 schema, the "obsolete" element goes away but everything else remains. So the XSLT needs to do two things:
Remove the tag
Change the namespace from http://foo/1.0 to http://foo/1.1
Here's my solution:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:foo1="http://foo/1.0"
xmlns:foo="http://foo/1.1"
exclude-result-prefixes="foo1">
<xsl:output method="xml" standalone="yes" indent="yes"/>
<!-- Remove the "obsolete" tag -->
<xsl:template match="foo1:rootElement/obsolete"/>
<!-- Copy the "alpha" tag -->
<xsl:template match="foo1:rootElement/stuff/alpha">
<alpha>
<xsl:apply-templates/>
</alpha>
</xsl:template>
<!-- Copy the "beta" tag -->
<xsl:template match="foo1:rootElement/stuff/beta">
<beta>
<xsl:apply-templates/>
</beta>
</xsl:template>
<!-- Copy the "stuff" tag -->
<xsl:template match="foo1:rootElement/stuff">
<stuff>
<xsl:apply-templates/>
</stuff>
</xsl:template>
<!-- Copy the "rootElement" tag -->
<xsl:template match="foo1:rootElement">
<foo:rootElement>
<xsl:apply-templates/>
</foo:rootElement>
</xsl:template>
</xsl:stylesheet>
Although this produces the output I want, notice that I have a copying template for every element in the schema: alpha, beta, etc. For complex schemas with hundreds of kinds of elements, I'd have to add hundreds of templates!
I thought I could eliminate this problem by reducing all those copying templates into a single identity transform, like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:foo1="http://foo/1.0"
xmlns:foo="http://foo/1.1"
exclude-result-prefixes="foo1">
<xsl:output method="xml" standalone="yes" indent="yes"/>
<xsl:template match="foo1:rootElement/obsolete"/>
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="foo1:rootElement/stuff">
<xsl:copy>
<xsl:call-template name="identity"/>
</xsl:copy>
</xsl:template>
<xsl:template match="foo1:rootElement">
<foo:rootElement>
<xsl:apply-templates/>
</foo:rootElement>
</xsl:template>
</xsl:stylesheet>
But it produces:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo:rootElement xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:foo="http://foo/1.1">
<stuff xmlns:foo="http://foo/1.0">
<stuff>
<alpha>a</alpha>
<beta>b</beta>
</stuff>
</stuff>
</foo:rootElement>
The "stuff" element was copied, which is what I want, but it's wrapped in another "stuff" element, tagged with the old namespace. I don't understand why this is happening. I've tried many ways of fixing this but have been unsuccessful. Any suggestions? Thanks.
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:strip-space elements="*"/>
<xsl:param name="pOldNS" select="'http://foo/1.0'"/>
<xsl:param name="pNewNS" select="'http://foo/1.1'"/>
<xsl:template match="/*">
<xsl:element name="{name()}" namespace="{$pNewNS}">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:copy-of select="namespace::*[not(.=$pOldNS)]"/>
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:choose>
<xsl:when test="namespace-uri()=$pOldNS">
<xsl:attribute name="{name()}" namespace="{$pNewNS}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="obsolete"/>
</xsl:stylesheet>
when applied on this XML document:
<foo:rootElement xmlns:foo="http://foo/1.0">
<obsolete>something</obsolete>
<stuff>
<alpha x="y" foo:t="z">a</alpha>
<beta>b</beta>
</stuff>
</foo:rootElement>
produces the wanted, correct result (obsolute elements deleted, namespace upgraded, attribute nodes correctly processed, including nodes that were in the old namespace):
<foo:rootElement xmlns:foo="http://foo/1.1">
<stuff>
<alpha x="y" foo:t="z">a</alpha>
<beta>b</beta>
</stuff>
</foo:rootElement>
Main advantages of this solution:
Works correctly when there are attributes, belonging to the old-schema namespace.
General, allows the old and new namespace to be passed as external parameters to the transformation.
While you might want to copy the attributes, elements need to be regenerated in the new namespace. So I'd suggest to do it like this:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:foo1="http://foo/1.0">
<xsl:template match="foo1:rootElement/obsolete"/>
<xsl:template match="attribute()">
<xsl:copy/>
</xsl:template>
<xsl:template match="element()">
<xsl:variable name="name" select="local-name()"/>
<xsl:element name="{$name}" namespace="http://foo/1.1">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>