Home > Enterprise >  How to rename attributes using xsl:perform-sort?
How to rename attributes using xsl:perform-sort?

Time:12-06

I have an XML document:

<graph>
  <v id="a"/>
  <v id="boo"/>
  <v id="boo"/>
  <v id="a"/>
</graph>

I'm trying to rename all @id attributes to unique numbers, which would represent the order in which attributes showed up in the document (actually, it may be any order, as long as the numbers I'll uniquely map to the textual values of the attributes):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:variable name="sorted">
    <xsl:perform-sort select="@*">
      <xsl:sort select="."/>
    </xsl:perform-sort>
  </xsl:variable>
  <xsl:template match="@*" priority="1">
    <xsl:attribute name="{name()}">
      <xsl:value-of select="index-of(distinct-values($sorted), .)"/>
    </xsl:attribute> 
  </xsl:template>
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

I'm expecting to get:

<graph>
  <v id="1"/>
  <v id="2"/>
  <v id="2"/>
  <v id="1"/>
</graph>

But my XSL doesn't even compile (however a very similar approach works for modifying node text contents, not attributes).

CodePudding user response:

Please try the following XSLT.

Input XML

<graph>
    <v id="a"/>
    <v id="boo"/>
    <v id="boo"/>
    <v id="a"/>
</graph>

XSLT 2.0

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="@id">
        <xsl:attribute name="{local-name()}">
            <!--<xsl:value-of select="translate(., 'abc', '123')"/>-->
            <xsl:value-of select="index-of(distinct-values(/graph/v/@id), .)"/>
        </xsl:attribute>
    </xsl:template>
</xsl:stylesheet>

Output

<graph>
  <v id="1"/>
  <v id="2"/>
  <v id="2"/>
  <v id="1"/>
</graph>

CodePudding user response:

If I follow this correctly, you are trying to do:

XSLT 2.0

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

  <xsl:variable name="sorted">
    <xsl:perform-sort select="/graph/v">
      <xsl:sort select="@id"/>
    </xsl:perform-sort>
  </xsl:variable>
  
  <xsl:template match="@id">
    <xsl:attribute name="id" select="index-of(distinct-values($sorted/v/@id), .)"/>
  </xsl:template>
  
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>
  
</xsl:stylesheet>

Note:

  • The context of a global variable is the root node;
  • Attributes must be children of some element'
  • If you don't care about the order, then sorting is a waste of time, CPU cycles and electricity. And so is the repeated application of distinct-values() on the same sequence.
  • Related