My goal is to be able to :
- Replace root element's
xmlns="http://xmlns.example.com/v1"
withxmlns="http://xmlns.example.com"
(without v1) - Replace root element's
schemaVersion="1"
withschemaVersion="2"
(version 2) - Apply the other template match to exclude (not add) the nodes I need to remove from input xml.
Input xml
<account xmlns="http://xmlns.example.com/v1"
xmlns:ns2="http://xmlns.example/v2"
schemaVersion="1">
<head>Jane Doe</head>
<accountNumber>1234567</accountNumber>
<messageType>CREATE</messageType>
<create>
<ns2:profile>
<ns2:accountNumber>1234567</ns2:accountNumber>
</ns2:profile>
</create>
<ns2:tax>
<ns2:taxExempt>false</ns2:taxExempt>
</ns2:tax>
<contact>
<ns2:address>US</ns2:address>
</contact>
</account>
Desired output xml
<account xmlns="http://xmlns.example.com"
xmlns:ns2="http://xmlns.example/v2"
schemaVersion="2">
<head>Jane Doe</head>
<messageType>ADD</messageType>
<action>A</action>
<ns2:profile>
<ns2:accountNumber>1234567</ns2:accountNumber>
</ns2:profile>
<contact>
<ns2:address>US</ns2:address>
</contact>
</account>
In the desired output, I
- Changed root element first namespace value from
xmlns="http://xmlns.example.com/v1"
toxmlns="http://xmlns.example.com"
(without v1) - Changed root element's
schemaVersion
attribute value fromschemaVersion="1"
toschemaVersion="2"
(version 2) - Removed
<accountNumber>
- Changed
<messageType>
value from CREATE to ADD - Added
<action>A</action>
- Removed parent node
<create>
but kept its children node. - Removed entire
<ns2:tax>
node and its children
I managed to produce the desired output. However, it introduced a problem.
Problem : In current output xml, xmlns="http://xmlns.example.com/v1"
(version 1) was added to <head>
and <contact>
.
xmlns="http://xmlns.example.com/v1"
should go away.
Current Output from my xsl
<account xmlns="http://xmlns.example.com"
xmlns:ns2="http://xmlns.example/v2"
schemaVersion="2">
<head xmlns="http://xmlns.example.com/v1">Jane Doe</head>
<messageType>ADD</messageType>
<action>A</action>
<ns2:profile>
<ns2:accountNumber>1234567</ns2:accountNumber>
</ns2:profile>
<contact xmlns="http://xmlns.example.com/v1">
<ns2:address>US</ns2:address>
</contact>
</account>
My XSL Code
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="http://xmlns.example.com/v1"
xmlns:ns2="http://xmlns.example/v2">
<xsl:output method="xml" encoding="utf-8" indent="yes" />
<xsl:strip-space elements="*" />
<!-- Copy all text nodes, elements and attributes -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<!-- Replace root's xmlns and schemaVersion value -->
<xsl:template match="account">
<account xmlns="http://xmlns.example.com"
xmlns:ns2="http://xmlns.example/v2"
schemaVersion="2">
<xsl:apply-templates />
</account>
</xsl:template>
<!-- remove <accountNumber> -->
<xsl:template match="accountNumber" />
<!-- Change messageType value from CREATE to ADD -->
<!-- Add <action>A</action> -->
<xsl:template
match="account/messageType[text()='CREATE']">
<xsl:element name='messageType'
namespace='http://xmlns.example.com'>
<xsl:value-of select="'ADD'" />
</xsl:element>
<xsl:element name='action'
namespace='http://xmlns.example.com'>
<xsl:value-of select="'A'" />
</xsl:element>
</xsl:template>
<!-- remove node <create> but keep its children -->
<xsl:template match="create">
<xsl:apply-templates />
</xsl:template>
<!-- Removed entire <ns2:tax> node and its children -->
<xsl:template match="ns2:tax" />
<!-- Ensure that the namespace of nodes prefixed with ns2 has "http://xmlns.example/v2" value -->
<xsl:template match="ns2:*">
<xsl:element name="ns2:{local-name()}"
namespace="http://xmlns.example/v2">
<xsl:apply-templates select="@*, node()" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
I'm having difficulty because I had to set xpath-default-namespace="http://xmlns.example.com/v1"
to version 1 first to match the nodes I want to remove AND I then eventually I need to replace the xmlns value to the not version 1 (xmlns="http://xmlns.example.com/"
)
I'd appreciate any suggestion or thoughts on solving this.
Thank you.
CodePudding user response:
The identity transformation alone doesn't help if you want or need to change the namespaces of elements (and a namespace declared on some element applies to all its descendants so you need to change the namespace of the root and all its descendants); based on that I think you need to write a templates as the starting point that change the element namespace:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://xmlns.example.com"
xmlns:v1="http://xmlns.example.com/v1"
xpath-default-namespace="http://xmlns.example.com/v1"
xmlns:ns2="http://xmlns.example/v2"
exclude-result-prefixes="v1">
<xsl:output method="xml" encoding="utf-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="/*/@schemaVersion">
<xsl:attribute name="{name()}">2</xsl:attribute>
</xsl:template>
<!-- Change namespace http://xmlns.example.com/v1 to http://xmlns.example.com-->
<xsl:template match="v1:*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="namespace::*[not(. = 'http://xmlns.example.com/v1')]"/>
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<!-- remove <accountNumber> -->
<xsl:template match="accountNumber" />
<!-- Change messageType value from CREATE to ADD -->
<!-- Add <action>A</action> -->
<xsl:template
match="account/mesageType[.='CREATE']">
<messageType>ADD</messageType>
<action>A</action>
</xsl:template>
<!-- remove node <create> but keep its children -->
<xsl:template match="create">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="ns2:*">
<xsl:copy copy-namespaces="no">
<xsl:copy-of select="namespace::*[not(. = 'http://xmlns.example.com/v1')]"/>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<!-- Removed entire <ns2:tax> node and its children -->
<xsl:template match="ns2:tax" />
</xsl:stylesheet>