Search code examples
xmlxslt

XSLT transformation - adding a node to an existing element


I'm trying to add a an extra node to an XML file using XSLT, but I have troubles with the namespaces.

Input XML

<?xml version="1.0" encoding="utf-8"?>
<AuditFile xmlns="urn:StandardAuditFile-Taxation-Financial:DK" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:doc="urn:schemas-OECD:schema-extensions:documentation">
  <Header>
    <AuditFileVersion>1.0</AuditFileVersion>
    <AuditFileCountry>DK</AuditFileCountry>
    <AuditFileDateCreated>2024-06-19</AuditFileDateCreated>
    <Company>
      <RegistrationNumber />
      <Name>usdk</Name>
      <Address>
        <StreetName />
        <Number />
        <City />
        <PostalCode />
        <Country>DK</Country>
        <AddressType>PostalAddress</AddressType>
      </Address>
      <Contact>
        <ContactPerson>
          <FirstName>NotUsed</FirstName>
          <LastName />
        </ContactPerson>
      </Contact>
      <TaxRegistration>
        <TaxRegistrationNumber />
        <TaxType>VAT</TaxType>
        <Country>DN</Country>
      </TaxRegistration>
    </Company>
    <DefaultCurrencyCode>EUR</DefaultCurrencyCode>
    <SelectionCriteria>
      <SelectionStartDate>2024-06-19</SelectionStartDate>
      <SelectionEndDate>2024-06-19</SelectionEndDate>
    </SelectionCriteria>
    <TaxAccountingBasis>Fakturaregnskab</TaxAccountingBasis>
    <TaxEntity>Afdeling</TaxEntity>
  </Header>
</AuditFile>

Desired output (adding Telephone to Contact)

<AuditFile xmlns="urn:OECD:StandardAuditFile-Taxation/2.00">
  <Header>
    <AuditFileVersion>1.0</AuditFileVersion>
    <AuditFileCountry>DK</AuditFileCountry>
    <AuditFileDateCreated>2024-06-19</AuditFileDateCreated>
    <Company>
      <RegistrationNumber />
      <Name>usdk</Name>
      <Address>
        <StreetName />
        <Number />
        <City />
        <PostalCode />
        <Country>DK</Country>
        <AddressType>PostalAddress</AddressType>
      </Address>
      <Contact>
        <ContactPerson>
          <FirstName>NotUsed</FirstName>
          <LastName />
        </ContactPerson>
        <Telephone>123456789</Telephone>
      </Contact>
      <TaxRegistration>
        <TaxRegistrationNumber />
        <TaxType>VAT</TaxType>
        <Country>DN</Country>
      </TaxRegistration>
    </Company>
    <DefaultCurrencyCode>EUR</DefaultCurrencyCode>
    <SelectionCriteria>
      <SelectionStartDate>2024-06-19</SelectionStartDate>
      <SelectionEndDate>2024-06-19</SelectionEndDate>
    </SelectionCriteria>
    <TaxAccountingBasis>Fakturaregnskab</TaxAccountingBasis>
    <TaxEntity>Afdeling</TaxEntity>
  </Header>
</AuditFile>

After some struggling I came up with the following XSLT:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:faia="urn:StandardAuditFile-Taxation-Financial:DK">
  <xsl:variable name="urnStandardAuditFile">urn:OECD:StandardAuditFile-Taxation/2.00</xsl:variable>
  
  <xsl:template match="*">
    <xsl:element name="{local-name()}" namespace="{$urnStandardAuditFile}">
    <xsl:apply-templates select="@*|node()"/>
    </xsl:element>
  </xsl:template>
  
  <xsl:template match="@*">
    <xsl:attribute name="{local-name()}">
      <xsl:value-of select="."/>
    </xsl:attribute>
  </xsl:template>
  
  <xsl:template match="text() | comment() | processing-instruction()">
    <xsl:copy/>
  </xsl:template>
  
  <xsl:template match="faia:Contact">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
      <Telephone></Telephone>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

But that keeps giving me nodes with namespaces, which I don't want.

<Contact xmlns="urn:StandardAuditFile-Taxation-Financial:DK">
    <ContactPerson xmlns="urn:OECD:StandardAuditFile-Taxation/2.00">
        <FirstName>NotUsed</FirstName>
        <LastName/>
    </ContactPerson>
    <Telephone xmlns="urn:OECD:StandardAuditFile-Taxation/2.00">123456789</Telephone>
    <Telephone xmlns=""/>
</Contact>

Any help would be very much appreciated.


Solution

  • I think that all you need to do is just:

    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:dk="urn:StandardAuditFile-Taxation-Financial:DK"
    xmlns="urn:StandardAuditFile-Taxation-Financial:DK">
    <xsl:output method="xml" indent="yes"/>
    
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="dk:Contact">
        <xsl:copy>
            <xsl:apply-templates/>
            <Telephone>123456789</Telephone>
        </xsl:copy>
    </xsl:template>
    
    </xsl:stylesheet>
    

    Take notice of the double declaration of the same namespace in the stylesheet:

    xmlns:dk="urn:StandardAuditFile-Taxation-Financial:DK"
    xmlns="urn:StandardAuditFile-Taxation-Financial:DK"
    

    The first one allows you to match the Contact element using the assigned prefix. The other declares a default namespace for the added Telephone element, so that it matches its parent's namespace.


    Added:

    If you also want to change the XML document's default namespace (IOW, to rename each and every element in the given input) you will need to do something like:

    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:dk="urn:StandardAuditFile-Taxation-Financial:DK"
    xmlns="urn:OECD:StandardAuditFile-Taxation/2.00"
    exclude-result-prefixes="dk">
    <xsl:output method="xml" indent="yes"/>
    
    <xsl:template match="*">
        <xsl:element name="{local-name()}">
            <xsl:apply-templates/>
        </xsl:element>
    </xsl:template>
    
    <xsl:template match="dk:Contact">
        <Contact>
            <xsl:apply-templates/>
            <Telephone>123456789</Telephone>
        </Contact>
    </xsl:template>
    
    </xsl:stylesheet>
    

    If it's important to have the additional namespace declarations at the root element (which aren't used anywhere in the document) then also add this template:

    <xsl:template match="/dk:AuditFile">
        <AuditFile xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:doc="urn:schemas-OECD:schema-extensions:documentation">
            <xsl:apply-templates/>
        </AuditFile>
    </xsl:template>