Home > Blockchain >  XML with 2 namespace. Issue with attributes and unable to replace namespace
XML with 2 namespace. Issue with attributes and unable to replace namespace

Time:07-06

I'm struggling to resolve an xml transformation that I'm working on. The Input XML has 2 namespaces which makes it challenging and I need to replace the input XML's namespace value to some other value.

Basically, I am trying to:

  1. Replace xmlns:ns2="http://xmlns.example.com/eventNotify/v1" with xmlns:ns2="http://xmlns.example.com/eventNotify"
  2. Remove a few nodes.
  3. Add a few nodes.

My output xml is almost close to what I want to achieve. Except that,

Problems:

  1. xmlns:ns2="http://xmlns.example.com/eventNotify/v1" is not changing to xmlns:ns2="http://xmlns.example.com/eventNotify"
  2. xmlns="http://xmlns.example.com/cds/customer" is missing in the namespace declaration of output xml
  3. In the output xml, some nodes has xmlns="http://xmlns.example.com/eventNotify" which shouldn't be

Example:

<enterpriseProfile domain="customer" majorVersion="0" minorVersion="30">

became

<enterpriseProfile xmlns="http://xmlns.example.com/eventNotify">customer030<accountNumber>613257179</accountNumber>
  1. Schema version is not changing to 21.22.35

Input XML

<?xml version="1.0" encoding="UTF-8"?>
<ns2:accountEventNotify xmlns:ns2="http://xmlns.example.com/eventNotify/v1" xmlns="http://xmlns.example.com/cds/customer" schemaVersion="1">
   <ns2:header>
      <ns2:employee>
         <ns2:opco>ABCD</ns2:opco>
         <ns2:number>1111111</ns2:number>
      </ns2:employee>
      <ns2:sourceSystem>SYS1</ns2:sourceSystem>
      <ns2:msgCreateTime>2022-06-15T16:58:30.599Z</ns2:msgCreateTime>
      <ns2:businessEvent>
         <ns2:event>maintenance</ns2:event>
      </ns2:businessEvent>
   </ns2:header>
   <ns2:accountNumber>123456789</ns2:accountNumber>
   <ns2:messageType>CREATE</ns2:messageType>
   <ns2:create>
      <enterpriseProfile domain="customer" majorVersion="0" minorVersion="30">
         <accountNumber>123456789</accountNumber>
         <profile>
            <customerType>AAA</customerType>
            <accountType>AAA</accountType>
            <accountStatus>
               <statusCode>ACTIVE</statusCode>
               <statusDate>2022-06-15</statusDate>
            </accountStatus>
            <creationDate>2022-06-15</creationDate>
            <originSource>FF</originSource>
            <accountLinkageFlag>false</accountLinkageFlag>
            <welcomeKit>
               <welcomeKitFlag>false</welcomeKitFlag>
            </welcomeKit>
         </profile>
      </enterpriseProfile>     
   </ns2:create>
</ns2:accountEventNotify>

XSL

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns="http://xmlns.example.com/eventNotify"
    xmlns:c1="http://xmlns.example.com/cds/customer"
    xmlns:ns2="http://xmlns.example.com/eventNotify/v1"
    xpath-default-namespace="http://xmlns.example.com/cds/customer"
    exclude-result-prefixes="c1">

    <!-- remove the empty lines with XSLT -->
    <xsl:output method="xml" encoding="utf-8" indent="yes" />
    <xsl:strip-space elements="*" />

    <!-- replace the schema version value -->
    <xsl:template match="/*/@schemaVersion">
        <xsl:attribute name="{name()}">21.22.35</xsl:attribute>
    </xsl:template>

    <xsl:template match="c1:*">
        <xsl:element name="{local-name()}">
            <xsl:copy-of
                select="namespace::*[not(. = 'http://xmlns.example.com/cds/customer')]" />
            <xsl:apply-templates select="@* | node()" />
        </xsl:element>
    </xsl:template>

    <!-- remove <accountNumber> -->
    <xsl:template match="ns2:accountNumber" />

    <!-- Change messageType value from CREATE to ADD -->
    <!-- Add <action>A</action> -->
    <xsl:template
        match="ns2:accountEventNotify/ns2:messageType[.='CREATE']">
        <ns2:messageType>ADD</ns2:messageType>
        <ns2:action>A</ns2:action>
    </xsl:template>

    <!-- remove node <ns2:create> but keep its children -->
    <xsl:template match="ns2: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/cds/customer')]" />
            <xsl:copy-of select="@*" />
            <xsl:apply-templates select="node()" />
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Output XML

<?xml version="1.0" encoding="utf-8"?>
<ns2:accountEventNotify xmlns:ns2="http://xmlns.example.com/eventNotify/v1" schemaVersion="1">
   <ns2:header>
      <ns2:employee>
         <ns2:opco>ABCD</ns2:opco>
         <ns2:number>1111111</ns2:number>
      </ns2:employee>
      <ns2:sourceSystem>SYS1</ns2:sourceSystem>
      <ns2:msgCreateTime>2022-06-15T16:58:30.599Z</ns2:msgCreateTime>
      <ns2:businessEvent>
         <ns2:event>maintenance</ns2:event>
      </ns2:businessEvent>
   </ns2:header>
   <ns2:messageType xmlns="http://xmlns.example.com/eventNotify">ADD</ns2:messageType>
   <ns2:action xmlns="http://xmlns.example.com/eventNotify">A</ns2:action>
   <enterpriseProfile xmlns="http://xmlns.example.com/eventNotify">customer030<accountNumber>613257179</accountNumber>
      <profile>
         <customerType>AAA</customerType>
         <accountType>AAA</accountType>
         <accountStatus>
            <statusCode>ACTIVE</statusCode>
            <statusDate>2022-06-15</statusDate>
         </accountStatus>
         <creationDate>2022-06-15</creationDate>
         <originSource>FF</originSource>
         <accountLinkageFlag>false</accountLinkageFlag>
         <welcomeKit>
            <welcomeKitFlag>false</welcomeKitFlag>
         </welcomeKit>
      </profile>
   </enterpriseProfile>
</ns2:accountEventNotify>

Desired output

<?xml version="1.0" encoding="utf-8"?>
<ns2:accountEventNotify xmlns:ns2="http://xmlns.example.com/eventNotify" xmlns="http://xmlns.example.com/cds/customer" schemaVersion="21.22.35">
   <ns2:header>
      <ns2:employee>
         <ns2:opco>ABCD</ns2:opco>
         <ns2:number>1111111</ns2:number>
      </ns2:employee>
      <ns2:sourceSystem>SYS1</ns2:sourceSystem>
      <ns2:msgCreateTime>2022-06-15T16:58:30.599Z</ns2:msgCreateTime>
      <ns2:businessEvent>
         <ns2:event>maintenance</ns2:event>
      </ns2:businessEvent>
   </ns2:header>
   <ns2:messageType>ADD</ns2:messageType>
   <ns2:action>A</ns2:action>
   <enterpriseProfile domain="customer" majorVersion="0" minorVersion="30">
      <profile>
         <customerType>AAA</customerType>
         <accountType>AAA</accountType>
         <accountStatus>
            <statusCode>ACTIVE</statusCode>
            <statusDate>2022-06-15</statusDate>
         </accountStatus>
         <creationDate>2022-06-15</creationDate>
         <originSource>FF</originSource>
         <accountLinkageFlag>false</accountLinkageFlag>
         <welcomeKit>
            <welcomeKitFlag>false</welcomeKitFlag>
         </welcomeKit>
      </profile>
   </enterpriseProfile>
</ns2:accountEventNotify>

CodePudding user response:

Try this:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:ns2="http://xmlns.example.com/eventNotify"
    xmlns:old="http://xmlns.example.com/eventNotify/v1"
    xmlns="http://xmlns.example.com/cds/customer"
    exclude-result-prefixes="old">

    <!-- remove the empty lines with XSLT -->
    <xsl:output method="xml" encoding="utf-8" indent="yes" />
    <xsl:strip-space elements="*" />
    
    <xsl:template match="* | @*">
      <xsl:copy copy-namespaces="no">
        <xsl:apply-templates select="@* | node()"/>
      </xsl:copy>
    </xsl:template>

    <!-- replace the schema version value -->
    <xsl:template match="/old:accountEventNotify">
      <ns2:accountEventNotify>
        <xsl:apply-templates select="@*"/>
        <xsl:attribute name="schemaVersion">21.22.35</xsl:attribute>
        <xsl:apply-templates/>
      </ns2:accountEventNotify>
    </xsl:template>

    <!-- remove <accountNumber> -->
    <xsl:template match="old:accountNumber" />

    <!-- Change messageType value from CREATE to ADD -->
    <!-- Add <action>A</action> -->
    <xsl:template
        match="old:accountEventNotify/old:messageType[.='CREATE']">
        <ns2:messageType>ADD</ns2:messageType>
        <ns2:action>A</ns2:action>
    </xsl:template>

    <!-- remove node <old:create> but keep its children -->
    <xsl:template match="old:create">
        <xsl:apply-templates />
    </xsl:template>

    <!-- rename elements from the old to the new namespace -->
    <xsl:template match="old:*">
        <xsl:element name="ns2:{local-name()}">
            <xsl:apply-templates select="@*" />
            <xsl:apply-templates select="node()" />
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

I think part of your problem may be that you were confused as to which namespaces were which. In this XSLT I bound the prefix old to the namespace bound to ns2 in the source document, and I think this makes it clearer.

I added a template matching the root element, with the effect that the http://xmlns.example.com/cds/customer namespace declaration appears on the root element in the output, since that seemed to be one of the things you wanted.

Using the input data from your question, my output was:

<?xml version="1.0" encoding="utf-8"?>
<ns2:accountEventNotify xmlns:ns2="http://xmlns.example.com/eventNotify" xmlns="http://xmlns.example.com/cds/customer" schemaVersion="21.22.35">
   <ns2:header>
      <ns2:employee>
         <ns2:opco>ABCD</ns2:opco>
         <ns2:number>1111111</ns2:number>
      </ns2:employee>
      <ns2:sourceSystem>SYS1</ns2:sourceSystem>
      <ns2:msgCreateTime>2022-06-15T16:58:30.599Z</ns2:msgCreateTime>
      <ns2:businessEvent>
         <ns2:event>maintenance</ns2:event>
      </ns2:businessEvent>
   </ns2:header>
   <ns2:messageType>ADD</ns2:messageType>
   <ns2:action>A</ns2:action>
   <enterpriseProfile domain="customer" majorVersion="0" minorVersion="30">
      <accountNumber>123456789</accountNumber>
      <profile>
         <customerType>AAA</customerType>
         <accountType>AAA</accountType>
         <accountStatus>
            <statusCode>ACTIVE</statusCode>
            <statusDate>2022-06-15</statusDate>
         </accountStatus>
         <creationDate>2022-06-15</creationDate>
         <originSource>FF</originSource>
         <accountLinkageFlag>false</accountLinkageFlag>
         <welcomeKit>
            <welcomeKitFlag>false</welcomeKitFlag>
         </welcomeKit>
      </profile>
   </enterpriseProfile>
</ns2:accountEventNotify>

CodePudding user response:

The xsl:namespace instruction adds namespace nodes to the output element, but it will never change the name of the output element. The name of the output element (that is, both its local name and its namespace URI) are determined by the instruction that creates the element. You are creating elements using <xsl:element name="{local-name()}"> and <xsl:copy>. In the first case this will give you a no-namespace element, in the second case it will give you an element with the same namespace as the original.

Focus on getting the (two-part) element and attribute names right, and let the namespace nodes / declarations look after themselves.

  • Related