0

I have an XML that looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<dataroot xmlns:od="urn:schemas-microsoft-com:officedata" generated="2020-08-28T20:55:05">
    <BISTA>
        <ID>6</ID>
        <version>1.0</version>
    </BISTA>
</dataroot>

I need to delete the 'dataroot' element, make the 'version' element an attribute and define a custom namespace for 'BISTA'. In the end it should look like this:

<BISTA xmlns="http://www.test.com" version="1.0">
    <ID>6</ID>
</BISTA>

The XSLT I am using is to achieve this is

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>

    <!-- template for the document element (omit root) --> 
    <xsl:template match="/*">   
        <xsl:apply-templates select="node()" /> 
    </xsl:template>   

    <!-- identity template --> 
    <xsl:template match="node() | @*">   
        <xsl:copy>
            <xsl:apply-templates select="node() | @*" />
        </xsl:copy>
    </xsl:template>

    <!-- elements of root to attributes -->
    <xsl:template match="BISTA">
       <xsl:element name="{name()}" namespace="http://www.test.com">
            <xsl:for-each select="version">
                <xsl:attribute name="{name(.)}">
                    <xsl:value-of select="."/>
                </xsl:attribute>
            </xsl:for-each>
        <xsl:apply-templates select="node() | @*"/>
     </xsl:element>
    </xsl:template>
    
    <!-- delete version element -->
    <xsl:template match="BISTA/version"/>
   
</xsl:stylesheet>

What I get from this is

<?xml version="1.0" encoding="UTF-8"?>
    <BISTA xmlns="http://www.test.com" version="1.0">
        <ID xmlns="" xmlns:od="urn:schemas-microsoft-com:officedata">6</ID>
        
    </BISTA>


Issues with this:

  • the indentation looks off
  • there is an empty namespace in 'ID'
  • the unwanted xmlns:od attribute

What am I doing wrong? How can I achieve the desired output?

1
  • I am new to XSLT and XML namespaces. If the answer to this follows from a classic reference, I happily take reading suggestions. Commented Aug 28, 2020 at 19:27

1 Answer 1

1

If you declare a default namespace on one element like BISTA it applies to children and descendants as well; if the shown wanted output is indeed as shown then you not only want to transform BISTA to get the new namespace but all its descendant elements as well.

So use an approach like

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="2.0">
    
  <xsl:param name="new-namespace" as="xs:string">http://www.test.com</xsl:param>
  
  <xsl:strip-space elements="*"/>
  <xsl:output indent="yes"/>

  <xsl:template match="/*">
      <xsl:apply-templates/>
  </xsl:template>
  
  <xsl:template match="BISTA">
      <xsl:element name="{local-name()}" namespace="{$new-namespace}">
          <xsl:apply-templates select="@*, version, node() except version"/>
      </xsl:element>
  </xsl:template>
  
  <xsl:template match="BISTA/version">
      <xsl:attribute name="{local-name()}" select="."/>
  </xsl:template>
  
  <xsl:template match="*">
      <xsl:element name="{local-name()}" namespace="{$new-namespace}">
          <xsl:apply-templates select="@* , node()"/>
      </xsl:element>
  </xsl:template>
  
</xsl:stylesheet>

Or make the last match="*" match="BISTA//*".

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks. I will give this a try. Regarding: "if the shown wanted output is indeed as shown". Is there any reason to not want that output?
@Stefan, it is fine but saying "define a custom namespace for 'BISTA'" with that output means you need to transform each descendant element as well to have the new namespace. In the input the BISTA elements and its descendants are in no namespace, in your wanted output as shown they are all in that namespace http://www.test.com. Your code transforming a single element to a new namespace but using the identity on the descendants has to lead to xmlns="" for the descendants.
got it. Thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.