Home > Mobile >  XSLT 2.0 changing the default namespace value of ROOT after applying templates
XSLT 2.0 changing the default namespace value of ROOT after applying templates

Time:05-10

My goal is to be able to :

  1. Replace root element's xmlns="http://xmlns.example.com/v1" with xmlns="http://xmlns.example.com" (without v1)
  2. Replace root element's schemaVersion="1"with schemaVersion="2" (version 2)
  3. 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

  1. Changed root element first namespace value from xmlns="http://xmlns.example.com/v1" to xmlns="http://xmlns.example.com" (without v1)
  2. Changed root element's schemaVersion attribute value from schemaVersion="1" to schemaVersion="2" (version 2)
  3. Removed <accountNumber>
  4. Changed <messageType> value from CREATE to ADD
  5. Added <action>A</action>
  6. Removed parent node <create> but kept its children node.
  7. 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>
  • Related