Search code examples
xmlxsltxslt-2.0

Issue in converting name/value xml to element xml using xsl


I want to convert a name/value xml to element xml using xsl. I tried every method which I could find. I refered this post, Convert Name/Value Pair XML to Elements using XSLT and made the changes, but my resultant xml doesn't show the key values.

Below are my code files:

xml file:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<METSM xmlns="metsmng" xmlns:ns2="http://www.w3.org/1999/xlink">
<Case id="MTMzo29276" ns2:href="https://metsm.com/api/case/MTMzo29276">
    <Column name="BU">SU</Column>
    <Column name="Summary">Hardware failure</Column>
    <Column name="Project">Servers</Column>
    <Column name="Priority">High</Column>
    <Column name="Status">Working</Column>
    <Column name="Open-Date">01/23/2017 23:11:16</Column>
</Case>
<Case id="MTMzo29739" ns2:href="https://metsm.com/api/case/MTMzo29739">
    <Column name="BU">AICM</Column>
    <Column name="Summary">Create a new profile</Column>
    <Column name="Project">Datacentre</Column>
    <Column name="Priority">Low</Column>
    <Column name="Status">Open</Column>
    <Column name="Open-Date">10/04/2010 00:00:00</Column>
</Case>
</METSM>

xsl:

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

<xsl:template match="/METSM">
<METSM>
   <xsl:for-each select="Case">
   <Case>
      <xsl:element name="{@id}">
         <xsl:value-of select="."/>
      </xsl:element>
      <xsl:for-each select="Column">
         <xsl:element name="{@name}">
            <xsl:value-of select="."/>
         </xsl:element>
      </xsl:for-each>
   </Case>
</xsl:for-each>
</METSM>
</xsl:template>
</xsl:stylesheet>

Output required:

<METSM>
<Case>
  <id> MTMzo29276 </id>
  <BU> SU </BU>
  <Summary> Hardware failure </Summary>
  ....
</Case>
<Case>
 ....
</Case>
</METSM>

What I am getting:

<?xml version="1.0" encoding="UTF-8"?>


    SU
    Hardware failed
    Servers
    High
    Working
    01/23/2017 23:11:16


    AICM
    Create a new profile
    Datacentre
    Low
    Open
    10/04/2010 00:00:00

I am not able to figure out the issue. How do I get the key names in my result xml file. Any help will be highly grateful. Thanks in advance.

EDIT: using below python code to generate the output xml file:

 from lxml import etree
 data = open(r'C:\Users\abc\Desktop\input-xsl.xsl')
 xslt_content = data.read()
 xslt_root = etree.XML(xslt_content)
 dom = etree.parse(r'C:\Users\abc\Desktop\input-xml.xml')
 transform = etree.XSLT(xslt_root)
 result = transform(dom)
 print(result)

Solution

  • It's because your XML is in the default namespace metsmng. Since you're using XSLT 2.0, you can use xpath-default-namespace...

    XSLT 2.0

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
      xpath-default-namespace="metsmng">
      <xsl:output method="xml" indent="yes"/>
    
      <xsl:template match="/METSM">
        <METSM>
          <xsl:for-each select="Case">
            <Case>
              <id><xsl:value-of select="@id"/></id>
              <xsl:for-each select="Column">
                <xsl:element name="{@name}">
                  <xsl:value-of select="."/>
                </xsl:element>
              </xsl:for-each>
            </Case>
          </xsl:for-each>
        </METSM>
      </xsl:template>
    </xsl:stylesheet>
    

    Also notice that I fixed your id element output.

    Output

    <METSM>
       <Case>
          <id>MTMzo29276</id>
          <BU>SU</BU>
          <Summary>Hardware failure</Summary>
          <Project>Servers</Project>
          <Priority>High</Priority>
          <Status>Working</Status>
          <Open-Date>01/23/2017 23:11:16</Open-Date>
       </Case>
       <Case>
          <id>MTMzo29739</id>
          <BU>AICM</BU>
          <Summary>Create a new profile</Summary>
          <Project>Datacentre</Project>
          <Priority>Low</Priority>
          <Status>Open</Status>
          <Open-Date>10/04/2010 00:00:00</Open-Date>
       </Case>
    </METSM>
    

    You mentioned in the comments you are using lxml. If so, XSLT 2.0 is not supported; you'll need to use XSLT 1.0. What you'll need to do is bind the namespace to a prefix and use that prefix in your XPath's...

    XSLT 1.0

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
      xmlns:m="metsmng" exclude-result-prefixes="m">
      <xsl:output method="xml" indent="yes"/>
    
      <xsl:template match="/m:METSM">
        <METSM>
          <xsl:for-each select="m:Case">
            <Case>
              <id><xsl:value-of select="@id"/></id>
              <xsl:for-each select="m:Column">
                <xsl:element name="{@name}">
                  <xsl:value-of select="."/>
                </xsl:element>
              </xsl:for-each>
            </Case>
          </xsl:for-each>
        </METSM>
      </xsl:template>
    </xsl:stylesheet>