Home > Software engineering >  How to rename multiple elements with different names in XSLT?
How to rename multiple elements with different names in XSLT?

Time:09-10

I'm trying to rename elements with different element names using XSLT. My input XML is lengthy, so making it short below. I'm stuck in this part, can someone provide suggestions on this, provided link below:

<B> <A>
         <Year>9</Year>
         <Age>40</Age>
         <Value>32923.38</Value>
         <Value1>135223</Value1>
         <Value2>35</Value2>
         <Value3>114</Value3>
      </A>
      <A>
          <Year>19</Year>
         <Age>30</Age>
         <Value>42578.40</Value>
         <Value1>123</Value1>
         <Value2>70</Value2>
         <Value3>115</Value3>
      </A>
      </B>

Expected output:

<c>
      <A>
         <day>9</day>
         <type>40</type>
         <key>32923.38</key>
         <level>135223</level>
         <pay>35</pay>
         <terms>114</terms>
      </A>
      <A>
          <day>19</day>
         <type>30</type>
         <key>42578.40</key>
         <level>123</level>
         <pay>70</pay>
         <terms>115</terms>
      </A>    
      </c>

XSLT templates I tried:

<xsl:template match="B/A">
        <C><xsl:apply-templates select="@*|node()" /></C>
    </xsl:template>

Here is the actual XSLT which is doing work of split based on particular element but stuck for above scenario, not knowing how to apply in code and make it work.

I tried multiple ways but none of them working when applied to my code, here is the link of the code below: https://xsltfiddle.liberty-development.net/gVAkJ4Q/4

Updated the original XSLT, since actual source code is very lengthy added link above containing the source code and XSLT.

CodePudding user response:

Here's a simple example you can use as your starting point:

XML

<B>
    <A>
        <Year>9</Year>
        <Age>40</Age>
        <Value>32923.38</Value>
    </A>
    <A>
        <Year>19</Year>
        <Age>30</Age>
        <Value>42578.40</Value>
    </A>
</B>

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="*"/>

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

<xsl:template match="/B">
    <C>
        <xsl:apply-templates/>
    </C>
</xsl:template>

<xsl:template match="Year">
    <day>
        <xsl:apply-templates/>
    </day>
</xsl:template>

<xsl:template match="Age">
    <type>
        <xsl:apply-templates/>
    </type>
</xsl:template>

<xsl:template match="Value">
    <key>
        <xsl:apply-templates/>
    </key>
</xsl:template>

<!-- and so on ... -->

</xsl:stylesheet>

Result

<?xml version="1.0" encoding="UTF-8"?>
<C>
   <A>
      <day>9</day>
      <type>40</type>
      <key>32923.38</key>
   </A>
   <A>
      <day>19</day>
      <type>30</type>
      <key>42578.40</key>
   </A>
</C>

P.S. If you have a great many elements to rename, you might prefer to reduce the code to just:

<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:variable name="new-name">
        <xsl:choose>
            <xsl:when test="name()='B'">C</xsl:when>
            <xsl:when test="name()='Year'">day</xsl:when>
            <xsl:when test="name()='Age'">type</xsl:when>
            <xsl:when test="name()='Value'">key</xsl:when>
            <!-- and so on ... -->
            <xsl:otherwise>
                <xsl:value-of select="name()"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:variable>
    <xsl:element name="{$new-name}">
        <xsl:apply-templates/>
    </xsl:element>
</xsl:template>

</xsl:stylesheet>

CodePudding user response:

Here is a short and generic, pure XSLT 1.0 solution:

<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:param name="pReps"          
      select="'|Year day|Age type|Value key|Value1 level|Value2 pay|Value3 terms|'"/>
 <xsl:variable name="vReps" select="document('')/*/xsl:param/@select"/>

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match=
              "A/*[contains(document('')/*/xsl:param/@select, concat('|', name(), ' '))]">
    <xsl:element name=
             "{substring-before(substring-after($vReps, concat('|', name(), ' ')), '|')}">
      <xsl:apply-templates select="node()|@*"/>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document:

<B>
    <A>
        <Year>9</Year>
        <Age>40</Age>
        <Value>32923.38</Value>
        <Value1>135223</Value1>
        <Value2>35</Value2>
        <Value3>114</Value3>
    </A>
    <A>
        <Year>19</Year>
        <Age>30</Age>
        <Value>42578.40</Value>
        <Value1>123</Value1>
        <Value2>70</Value2>
        <Value3>115</Value3>
    </A>
</B>

the wanted, correct result is produced:

<B>
   <A>
      <day>9</day>
      <type>40</type>
      <key>32923.38</key>
      <level>135223</level>
      <pay>35</pay>
      <terms>114</terms>
   </A>
   <A>
      <day>19</day>
      <type>30</type>
      <key>42578.40</key>
      <level>123</level>
      <pay>70</pay>
      <terms>115</terms>
   </A>
</B>

Do note:

  1. This is a generic solution having only two simple templates. The element names and their replacements are not hardcoded in the transformation (and there are no "<!-- and so on ... -->" comments) , but are specified as the value of a global parameter, which can be passed externally to the transformation and can be different every time the same, unchanged transformation is executed.

  2. The "identity template" matches all nodes, whose names are not subject to renaming and shallow-copies them "as-is"

  3. Another template matches any element that is a child of A and whose name can be found in the externally passed parameter $pReps. This template finds the suggested replacement immediately after the delimited original name, extracts it and uses it in an <xsl:element> instruction to create an element that has the specified corresponding new name.

  4. The format to represent the old element names and their suggested replacements is:

"|{oldName} {newName}|"

  • Related