0

I want to convert my XML document but not sure whether the desired output can be obtained using XSLT. Below is my XML code:

    <GetInvoiceList>
    <Request>
    <Case>
    <id>Case_1</id>
    <CaseID>Hi</CaseID>
    </Case>
    <BillStatusCode>
    <BillStatusCode>type description</BillStatusCode>
    <typecode>1</typecode>
    </BillStatusCode>
    <EBillProcessStatusCode>
    <EBillProcessStatusCode>type description</EBillProcessStatusCode>
    <typecode>2</typecode>
    </EBillProcessStatusCode>
    </Request>
    </GetInvoiceList>

I want to convert it into this:

   <GetInvoiceList>
   <Request>
   <Case id="Case_1">
   <CaseID>Hi</CaseID>
   </Case>
   <BillStatusCode typecode="1">type description</BillStatusCode> 
   <EBillProcessStatusCode typecode="2">type description</EBillProcessStatusCode>
   </Request>
   </GetInvoiceList>

Is it possible to get the desired output? Would appreciate any help regarding this. Thanks!

2
  • 1
    Would it be possible for you to edit your question to explain the logic of the transformation (which is especially important if you want a generic solution)? Why does id become an attribute on Case, for example, but not CaseID. Similarly, why does typecode become an attribute, but BillStatusCode becomes a text node? Thank you. Commented Apr 8, 2015 at 8:28
  • Actually I was having one XML code which I converted into the one which is provided above. Now I want the reverse of it. We can modify the Input document if we want to distinguish nodes which are to be converted into attributes from those which are to be written as text nodes. I used conditional statements to convert it earlier. Now the reverse is needed. Commented Apr 8, 2015 at 9:02

2 Answers 2

1

If I understand correctly, the "generic solution" to undo the mess you have created would do two things:

  1. Convert id and typecode elements to attributes of their parent element;

  2. Eliminate any element whose name is the same as the name of its parent element, and copy its child nodes (text nodes in your example) to the parent element.

This translates to:

XSLT 1.0

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

<xsl:template match="*">
    <xsl:copy>
        <xsl:for-each select="id | typecode">
            <xsl:attribute name="{name()}">
                <xsl:value-of select="."/>
            </xsl:attribute>
        </xsl:for-each>
        <xsl:apply-templates select="node()[not(self::id or self::typecode)]"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="*[name(.) = name(..)]">
    <xsl:apply-templates/>
</xsl:template>

</xsl:stylesheet>
Sign up to request clarification or add additional context in comments.

Comments

0

Try it!!!, now I've changed the coding based on the element position. If you change the element positions in the input this won't help you.

Regards Bala

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

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

  <xsl:template match="*" mode="all">
    <xsl:choose>
      <xsl:when test="(count(ancestor::*) = 3 and count(preceding::*) = 0)
                      or (count(ancestor::*) = 3 and count(preceding::*) > 1 and count(preceding-sibling::*) = 1)">
      </xsl:when>
      <xsl:when test="count(ancestor::*) = 3 and count(preceding-sibling::*) = 0">
        <xsl:apply-templates mode="all"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy>
          <xsl:choose>
            <xsl:when test="count(ancestor::*) = 2 and count(preceding-sibling::*) = 0">
              <xsl:attribute name="{*[1]/name()}" select="*[1]"/>
            </xsl:when>
            <xsl:when test="count(ancestor::*) = 2 and count(preceding-sibling::*) != 0">
              <xsl:attribute name="{*[2]/name()}" select="*[2]"/>
            </xsl:when>
          </xsl:choose>
          <xsl:apply-templates mode="all"/>
        </xsl:copy>        
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

3 Comments

Thanks Bala for your response. Your code is working fine but I need a generic solution for this.
Code changed check it now.
Thanks Bala. But its a long code & I won't be able to specify positions for all elements which are to be converted. So, Instead of mentioning positions I would better mention the names. Michael's code is working fine. I will go with that one. Thanks a lot for your time.

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.