Kyle Souza
Kyle Souza

Reputation: 126

Renaming XML nodes with XSLT based on "name" attribute

I have this xml:

<?xml version="1.0" encoding="utf-8"?>
<Document Id="0c744468-67d8-4daa-8ff9-cbd23209c59d" Name="ROW_Easement (1)" TypeId="adde4dc1-0710-452a-82c7-9e5ac1bafe94" TypeName="ROW_Easement" OriginalName="106-19-47A.pdf" MimeType="application/pdf">
  <Field Name="File Name" Confidence="1.00" Page="1" Valid="True">106-19-47A</Field>
  <Section Name="tblPersonOfInterest">
    <SectionCollection Name="From" Count="4">
      <Section Name="From 1">
        <Field Name="Grantor" Confidence="1.00" Page="1" Valid="True" Location="1.713, 8.200, 6.487, 0.500">MARY E. GIBSON, and husband, E. J. GIBSON;
 ROSALIE L. SIEN, and husband, A. C. SIEN, Jr</Field>
      </Section>
     </SectionCollection>
   </Section>
</Document>

I want to replace all the node names with the "Name" attribute value. So far I have:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">
  <xsl:output method="xml" indent="yes" />
  <xsl:template match="@*|node()">
    <ROW_Easement>
      <xsl:for-each select="Field">
        <xsl:element name="{translate(@Name, ' ', '_')}">
          <xsl:value-of select="self::node()" />
        </xsl:element>
      </xsl:for-each>
      <xsl:for-each select="Section">
        <xsl:element name="{translate(@Name, ' ', '_')}">
          <xsl:value-of select="self::node()" />
        </xsl:element>
      </xsl:for-each>
      <xsl:for-each select="SectionCollection">
        <xsl:element name="{translate(@Name, ' ', '_')}">
          <xsl:value-of select="self::node()" />
        </xsl:element>
      </xsl:for-each>
    </ROW_Easement>
  </xsl:template>
</xsl:stylesheet>

It is resulting in:

<?xml version="1.0" encoding="utf-8"?>
<ROW_Easement>
  <File_Name>106-19-47A</File_Name>
  <tblPersonOfInterest>
    
      
        MARY E. GIBSON, and husband, E. J. GIBSON;
 ROSALIE L. SIEN, and husband, A. C. SIEN, Jr
      
     
   </tblPersonOfInterest>
</ROW_Easement>

It's breaking at the SectionCollection node, but I don't see why.

UPDATED per @michael.hor257k's suggestion UPDATE: Using the first suggestion I now get:

<?xml version="1.0" encoding="utf-8"?><ROW_Easement>0c744468-67d8-4daa-8ff9-cbd23209c59dROW_Easement (1)adde4dc1-0710-452a-82c7-9e5ac1bafe94ROW_Easement106-19-47A.pdfapplication/pdf
  <File_Name>1.001True106-19-47A</File_Name>
  <tblPersonOfInterest>
    <From>4
      <From_1>
        <Grantor>1.001True1.713, 8.200, 6.487, 0.500MARY E. GIBSON, and husband, E. J. GIBSON;
 ROSALIE L. SIEN, and husband, A. C. SIEN, Jr</Grantor>
      </From_1>
     </From>
   </tblPersonOfInterest>
</ROW_Easement>

Upvotes: 0

Views: 56

Answers (3)

Martin Honnen
Martin Honnen

Reputation: 167716

There is no explanation of which attributes you want to keep but based on your sample input and the code you later posted it seems you don't want to keep any attribute but the Name one on Document so

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">

  <xsl:template match="Document">
    <ROW_Easement Name="{@Name}">
        <xsl:apply-templates/>
    </ROW_Easement>
  </xsl:template>
  
  <xsl:template match="*">
    <xsl:element name="{translate(@Name, ' ', '_')}">
        <xsl:apply-templates/>
    </xsl:element>
  </xsl:template>
  
</xsl:stylesheet>

might be shorter and avoid the listing of all the attributes you don't want to copy.

Upvotes: 1

Kyle Souza
Kyle Souza

Reputation: 126

It may not be the most elegant solution, but using @zx485's answer I changed my XSLT to:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">
  <xsl:mode on-no-match="shallow-copy" />
  <!-- Remove unnecessary attributes -->
  <xsl:template match="@Confidence" />
  <xsl:template match="@Id" />
  <xsl:template match="@TypeId" />
  <xsl:template match="@TypeName" />
  <xsl:template match="@OriginalName" />
  <xsl:template match="@MimeType" />
  <xsl:template match="@Page" />
  <xsl:template match="@Valid" />
  <xsl:template match="@Count" />
  <xsl:template match="@Location" />
  
  <!-- Rename nodes -->
  <xsl:template match="Document">
    <ROW_Easement>
        <xsl:apply-templates select="node()|@*" />
    </ROW_Easement>
  </xsl:template>
  
  <!-- Copy to new XML -->
  <xsl:template match="*">
    <xsl:element name="{translate(@Name, ' ', '_')}">
        <xsl:apply-templates select="node()|@*[local-name()!='Name']" />
    </xsl:element>
  </xsl:template>
  
</xsl:stylesheet>

and it now works! Still open to suggestions for ways to improve if anyone wants to give them. Thank you all.

Upvotes: 0

zx485
zx485

Reputation: 29052

Assuming a well-formed XML input, you can use this simple XSLT-3.0 code. It replaces all element's names with the @Name attribute and also removes only this attribute from the output. The rest is copied as is by the xsl:mode:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">
  <xsl:mode on-no-match="shallow-copy" />
  
  <xsl:template match="Document">
    <ROW_Easement>
        <xsl:apply-templates select="node()|@*" />
    </ROW_Easement>
  </xsl:template>
    
  <xsl:template match="*">
    <xsl:element name="{translate(@Name, ' ', '_')}">
        <xsl:apply-templates select="node()|@*[local-name()!='Name']" />
    </xsl:element>
  </xsl:template>
  
</xsl:stylesheet>

Output is:

<?xml version="1.0" encoding="UTF-8"?>
<ROW_Easement Id="0c744468-67d8-4daa-8ff9-cbd23209c59d"
              Name="ROW_Easement (1)"
              TypeId="adde4dc1-0710-452a-82c7-9e5ac1bafe94"
              TypeName="ROW_Easement"
              OriginalName="106-19-47A.pdf"
              MimeType="application/pdf">
   <File_Name Confidence="1.00" Page="1" Valid="True">106-19-47A</File_Name>
   <tblPersonOfInterest>
      <From Count="4">
         <From_1>
            <Grantor>
          </Grantor>
         </From_1>
      </From>
   </tblPersonOfInterest>
</ROW_Easement>

Upvotes: 1

Related Questions