xslt namespace instruction causing unwanted side affects - xslt-2.0

Thanks to a couple of other questions I'm very slowly getting where I need to be with an xsl transformation to change the url of my namespaces. I am using xslt v2.
The main sample is here http://xsltransform.net/ei5Pwj2
This is starting to work but I have 2 questions 1 to try and make it work (!) and one to see if it is possible to make it better.
Firstly my use of
<xsl:namespace name="ns1">http://fruit.com/app/api</xsl:namespace>
has caused a problem because it has caused the ns1 attributes in the element to have their namepaces modified from
... ns1:created="2016-05-23T16:47:55+01:00" ns1:href="http://falseserver:8080/app/api/apple/1" ns1:id="1">
to
... ns1_1:created="2016-05-23T16:47:55+01:00"
ns1_2:href="http://falseserver:8080/app/api/apple/1" ns1_3:id="1">
Can anyone tell me why and how to stop this ?! I can't see how without adding it as a namespace in the element but as I have a namespace tag there already this is not possible
This would be enough to get me going for now but what woudl be perfect is if there is a way to transform the namespaces without reference to the element at all. At the moment if I can get it working as is I will need a few xslt files for slightly different documents. What I really want to do is transform the namespaces regardless of what the current root node is
so all documents would have all 6 namespaces as attributes regardless of whether the root element is
<ns2:apple ...
<ns2:apples ...
<ns4:banana ...
<ns4:bananas ...
etc.

You will need to transform any node that is in a certain namespace to the new namespace you need, it doesn't help or suffice to add namespaces to change the qualified name of a node (that qualified name always is a local name plus a namespace):
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns1="http://veg.com/app/api" xmlns:ns2="http://veg.com/app/api/apple">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ns2:*">
<xsl:element name="ns2:{local-name()}" namespace="http://fruit.com/app/api/apple">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="/ns2:*">
<xsl:element name="ns2:{local-name()}" namespace="http://fruit.com/app/api/apple">
<xsl:namespace name="ns1">http://fruit.com/app/api</xsl:namespace>
<xsl:namespace name="ns3">http://fruit.com/app/api/apple/red</xsl:namespace>
<xsl:namespace name="ns4">http://fruit.com/app/banana</xsl:namespace>
<xsl:namespace name="ns5">http://fruit.com/app/api/pear</xsl:namespace>
<xsl:namespace name="ns6">http://fruit.com/app/api/orange</xsl:namespace>
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="#ns1:*">
<xsl:attribute name="ns1:{local-name()}" namespace="http://fruit.com/app/api" select="."/>
</xsl:template>
</xsl:stylesheet>
http://xsltransform.net/ei5Pwj2/1
Your sample does not have nodes in the other namespaces but if the real code has such nodes and they need to be transformed then you need to add templates matching and transforming them, using the same approach as done above for the ns2:* element or ns1:* attribute nodes.

Related

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

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>

Simple XSLT Transformation When XML elements have text nodes and child nodes

I am new to xslt and am trying to transform the following xml:
<li>Hi there <person>David</person> , how is it going? </li>
I would like to transform this to another xml to something like:
<response>Hi there PERSON_NAME , how is it going? </response>
What I have so far is this:
<xsl:template match="li">
<response><xsl:value-of select="text()"/>
<xsl:choose>
<xsl:when test="person">
<xsl:text> PERSON_NAME </xsl:text>
</xsl:when>
</xsl:choose>
</response>
</xsl:template>
This is the output I get:
<response>Hi there , how is it going? PERSON_NAME</response>
Not exactly what I wanted. I am new to xslt and read a book. I did not find any example where there was a situation where an xml element had a child node in between its text value. Not sure if xslt can handle this or I am missing something fundamental. Any help would be greatly appreciated. I am using xslt 2.0
You can simply define two template to handle your condition.
<xsl:template match="li">
<response>
<xsl:apply-templates/>
</response>
</xsl:template>
<xsl:template match="person">
<xsl:text>PERSON_NAME</xsl:text>
</xsl:template>

xslt - strange thing when place root tag out of the <xsl:template>

I get xml from url: http://www.concert.ru/mail-ru/concert.xml
And I need tags ActionPlaces and Actions to be handled in separate manner - so I use two different tags for them:
<xsl:template match="/">
<xsl:apply-templates select="Data/ActionPlaces"/>
<xsl:apply-templates select="Data/Actions"/>
</xsl:template>
But they should be enveloped inside tag called enfinity
So when I do like this:
<enfinity>
<xsl:template match="Data/Actions">..
<xsl:template match="Data/ActionPlaces"> ..
</enfinity>
I get incorrect output. When the main tag is inside one of templates - I get correct output - but need main tag to be the top. How to handle it?
Try:
<xsl:template match="/">
<enfinity>
<xsl:apply-templates select="Data/ActionPlaces"/>
<xsl:apply-templates select="Data/Actions"/>
</enfinity>
</xsl:template>

XSLT Removing element with attribute but keep the children

I need to transform xml/html files into dita files. I want to remove some nodes but keep their children. The difficulties are:
Node I want to remove have attributes. I get error:
An attribute node (class) cannot be created after a child of the
containing element
And these attributes are unpredictable: I want to remove a variety of nodes, and I can't predict what kinds of attributes they have.
I don't know how deeply the node is nested in. It could be direct child of <body> or could be nested 4 or 5 levels down inside some other nodes.
XML Example:
<macro name="section">
<rich-text-body>
<macro name="column">
<parameter name="width">80%</parameter>
<rich-text-body>
<p>horribly nested, <span>bulky</span> structure</p>
<div>horribly nested, <span>bulky</span> structure</div>
</rich-text-body>
</macro>
</rich-text-body>
</macro>
I want to remove the bulky macro tags, but keep only the children of the most inner <rich-text-body>. In this case, they are the <p> <div> tags.
This is as far as I got. The XSLT
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="macro[#name='column' and parameter[#name='width'] ='80%']">
<xsl:apply-templates select="node()|#*"/>
</xsl:template>
Any help is appreciated! Thanks!
As already suggested, if you change
<xsl:template match="macro[#name='column' and parameter[#name='width'] ='80%']">
<xsl:apply-templates select="node()|#*"/>
</xsl:template>
to
<xsl:template match="macro[#name='column' and parameter[#name='width'] ='80%']">
<xsl:apply-templates select="node()"/>
</xsl:template>
respectively the equivalent but shorter
<xsl:template match="macro[#name='column' and parameter[#name='width'] ='80%']">
<xsl:apply-templates/>
</xsl:template>
then you won't get any errors from the attributes of the macro element as they are not processed.
If you want to process them to add them to a different element then you need to show us exactly where you want to put them.

xslt transform that selects element based upon negative value in multiple descendants

I have the following basic xml which I to parse to give the NAME only if none of the DB values = DB1.
<rnas>
<rna ID="1">
<NAME>Segment 6</NAME>
<XREF>
<ID>AF389120</ID>
<DB>DB1</DB>
</XREF>
<XREF>
<ID>ABCDE</ID>
<DB>DB2</DB>
</XREF>
</rna>
<rna ID="10">
<NAME>Segment 3</NAME>
<XREF>
<ID>12345</ID>
<DB>DB2</DB>
</XREF>
<XREF>
<ID>66789</ID>
<DB>DB3</DB>
</XREF>
</rna>
</rnas>
The expected output would be:
<rnas>
<rna ID="10">
<NAME>Segment 3</NAME>
</rna>
<rnas>
I am still a relative newbie and have tried a variety of approaches using XSLT 2.0 but so far have not been able to get anything to work properly. Any help would be much appreciated.
This will do what you want
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="rna[.//DB/text()='DB1']"/>
<xsl:template match="XREF"/>
</xsl:stylesheet>
It's an Identity Transform along with two empty templates. The first matches any rna that contains a DB with the text value DB1, and suppresses it. The second matches all XREF elements, which you do not want to output.

Resources