1

I am trying to convert attributes into elements, along with this, I want to change namespace prefix of my XML code. XML code:

    <lm:GetInvoiceList xmlns:lm="http://www.w3.org">
    <lm:Response>
    <lm:Bill>
    <lm:BillStatusCode typecode="1">type description</lm:BillStatusCode>
    <lm:EBillProcessStatusCode typecode="2">type description</lm:EBillProcessStatusCode>
    <lm:BillCycleCode typecode="1">type description</lm:BillCycleCode>
    <lm:BillActivityCode typecode="3">type description</lm:BillActivityCode>
    <lm:ToDate>...</lm:ToDate>
    </lm:Bill>
    </lm:Response>
    </lm:GetInvoiceList>

I have this XSLT code:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="node()">
    <xsl:copy>
    <xsl:apply-templates/>
    </xsl:copy>
    </xsl:template>

    <xsl:template match="*[@*]">
    <xsl:copy>
    <xsl:element name="ns:{name()}">
    <xsl:apply-templates select="node()"/>
    </xsl:element>
    <xsl:apply-templates select="@*"/>
    </xsl:copy>
    </xsl:template>
    <xsl:template match="*|*[@*]">
    <xsl:element name="ns:{local-name()}" namespace="http://my.ns.uri">
    <xsl:copy-of select="namespace::*"/>
    <xsl:apply-templates select="node()|@*"/>
    </xsl:element>
    </xsl:template>

    <xsl:template match="@*">
    <xsl:element name="ns:{name()}" namespace="http://my.ns.uri">
    <xsl:copy-of select="namespace::*"/>
    <xsl:value-of select="."/>
    </xsl:element>
    </xsl:template>
    </xsl:stylesheet>

But I am not getting the desired output.

Expected Output:

    <ns:GetInvoiceList xmlns:ns="http://my.ns.uri">
    <ns:Response>
    <ns:Bill>
    <ns:BillStatusCode>
    <ns:BillStatusCode>type description</ns:BillStatusCode>
    <ns:typecode>1</ns:typecode>
    </ns:BillStatusCode>
    <ns:EBillProcessStatusCode>
    <ns:EBillProcessStatusCode>type description</ns:EBillProcessStatusCode>
    <ns:typecode>2</ns:typecode>
    </ns:EBillProcessStatusCode>
    <ns:BillCycleCode>
    <ns:BillCycleCode>type description</ns:BillCycleCode>
    <ns:typecode>1</ns:typecode>
    </ns:BillCycleCode>
    <ns:BillActivityCode>
    <ns:BillActivityCode>type description</ns:BillActivityCode>
    <ns:typecode>3</ns:typecode>
    </ns:BillActivityCode>
    <ns:ToDate>...</ns:ToDate>
    </ns:Bill>
    </ns:Response>
    </ns:GetInvoiceList>

Actual output:

   <ns:GetInvoiceList xmlns:ns="http://my.ns.uri">
   <ns:Response>
   <ns:Bill>
   <ns:BillStatusCode>
   <ns:typecode>1</ns:typecode>type description</ns:BillStatusCode>
   <ns:EBillProcessStatusCode>
   <ns:typecode>2</ns:typecode>type description</ns:EBillProcessStatusCode>
   <ns:BillCycleCode>
   <ns:typecode>1</ns:typecode>type description</ns:BillCycleCode>
   <ns:BillActivityCode>
   <ns:typecode>3</ns:typecode>type description</ns:BillActivityCode>
   <ns:ToDate>...</ns:ToDate>
   </ns:Bill>
   </ns:Response>
   </ns:GetInvoiceList>

Would Appreciate any help on this!

4
  • Your question is not quite clear: what is the expected result if an element has an attribute, but no text node? Commented May 25, 2015 at 5:31
  • In that case that attribute should get converted into element. Commented May 25, 2015 at 5:43
  • Yes, but should this new element be placed within the original element - effectively duplicating the original element, the same way your example does for elements that have both text and an attribute? Commented May 25, 2015 at 6:07
  • Right. New element will be placed inside the parent element. Commented May 25, 2015 at 7:06

2 Answers 2

3

In view of your clarifications in the comments, I would suggest:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns="http://my.ns.uri">
<xsl:output method="xml" version="1.0" omit-xml-declaration="yes" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="*">
    <xsl:element name="ns:{local-name()}">
        <xsl:apply-templates/>
    </xsl:element>
</xsl:template>

<xsl:template match="*[@*]">
    <xsl:element name="ns:{local-name()}">
        <xsl:element name="ns:{local-name()}">
            <xsl:apply-templates/>
        </xsl:element>
        <xsl:apply-templates select="@*"/>
    </xsl:element>
</xsl:template>

<xsl:template match="@*">
    <xsl:element name="ns:{local-name()}">
        <xsl:value-of select="."/>
    </xsl:element>
</xsl:template>

</xsl:stylesheet>

What's the difference?

When applied to the following example input:

XML

<lm:GetInvoiceList xmlns:lm="http://www.w3.org">
   <lm:Response>
      <lm:Bill>
         <lm:BillPropertyA subPropertyA="first subproperty">first property</lm:BillPropertyA>
         <lm:BillPropertyB subPropertyB="second subproperty"/>
         <lm:BillPropertyC>second property</lm:BillPropertyC>
      </lm:Bill>
   </lm:Response>
</lm:GetInvoiceList>

the result will be:

<ns:GetInvoiceList xmlns:ns="http://my.ns.uri">
   <ns:Response>
      <ns:Bill>
         <ns:BillPropertyA>
            <ns:BillPropertyA>first property</ns:BillPropertyA>
            <ns:subPropertyA>first subproperty</ns:subPropertyA>
         </ns:BillPropertyA>
         <ns:BillPropertyB>
            <ns:BillPropertyB/>
            <ns:subPropertyB>second subproperty</ns:subPropertyB>
         </ns:BillPropertyB>
         <ns:BillPropertyC>second property</ns:BillPropertyC>
      </ns:Bill>
   </ns:Response>
</ns:GetInvoiceList>

as opposed to:

<ns:GetInvoiceList xmlns:ns="http://my.ns.uri">
   <ns:Response>
      <ns:Bill>
         <ns:BillPropertyA>
            <ns:subPropertyA>first subproperty</ns:subPropertyA>
            <ns:BillPropertyA>first property</ns:BillPropertyA>
         </ns:BillPropertyA>
         <ns:BillPropertyB>
            <ns:subPropertyB>second subproperty</ns:subPropertyB>
         </ns:BillPropertyB>
         <ns:BillPropertyC>
            <ns:BillPropertyA>second property</ns:BillPropertyA>
         </ns:BillPropertyC>
      </ns:Bill>
   </ns:Response>
</ns:GetInvoiceList>

which places the "second property" text node incorrectly under another instance of ns:BillPropertyA and makes that a child of ns:BillPropertyC.

Notes:

  1. If you want to change the namespace of a node, you cannot use xsl:copy, since that would also copy the existing namespace;

  2. You can (and should) declare the namespace once, then use the already existing binding where necessary.

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

3 Comments

Thanks for your time. Your code is working fine, but it is not copying text of the first element. For other elements it is working properly. Kirill answer is providing the desired output, So, I will use that. Thank you!
"it is not copying text of the first element." Isn't it? xsltransform.net/jyH9rMQ
Oh I am so sorry. That was my fault. It got erased from the source code by mistake. Thanks for your solution.
1

Try this XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output omit-xml-declaration="yes" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="*">
    <xsl:element name="ns:{local-name()}" namespace="http://my.ns.uri">
      <xsl:apply-templates select="@* | node()"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="text()">
    <xsl:element name="ns:{local-name(../../*)}" namespace="http://my.ns.uri">
      <xsl:value-of select="."/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="@*">
    <xsl:element name="ns:{local-name()}" namespace="http://my.ns.uri">
      <xsl:value-of select="."/>
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

Output:

<ns:GetInvoiceList xmlns:ns="http://my.ns.uri">
  <ns:Response>
    <ns:Bill>
      <ns:BillStatusCode>
        <ns:typecode>1</ns:typecode>
        <ns:BillStatusCode>type description</ns:BillStatusCode>
      </ns:BillStatusCode>
      <ns:EBillProcessStatusCode>
        <ns:typecode>2</ns:typecode>
        <ns:BillStatusCode>type description</ns:BillStatusCode>
      </ns:EBillProcessStatusCode>
      <ns:BillCycleCode>
        <ns:typecode>1</ns:typecode>
        <ns:BillStatusCode>type description</ns:BillStatusCode>
      </ns:BillCycleCode>
      <ns:BillActivityCode>
        <ns:typecode>3</ns:typecode>
        <ns:BillStatusCode>type description</ns:BillStatusCode>
      </ns:BillActivityCode>
      <ns:ToDate>
        <ns:BillStatusCode>...</ns:BillStatusCode>
      </ns:ToDate>
    </ns:Bill>
  </ns:Response>
</ns:GetInvoiceList>

1 Comment

Thanks a lot. It is generating the required output.

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.