Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSL: Determine sequence across ancestors

Tags:

xml

xslt

xslt-1.0

I have an XML sample that looks like:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <location id="1">
        <address>1600 Pennsylvania Avenue</address>
        <address>211B Baker Street</address>
    </location>
    <location id="1">
        <address>17 Cherry Tree Lane</address>
    </location>
    <location id="2">
        <address>350 5th Avenue</address>
    </location>
</root>

And I'd like to generate output that looks like:

<?xml version="1.0" encoding="utf-8"?>
<result>
    <location id="1">
        <address addressId="1">1600 Pennsylvania Avenue</address>
        <address addressId="2">211B Baker Street</address>
    </location>
    <location id="1">
        <address addressId="3">17 Cherry Tree Lane</address>
    </location>
    <location id="2">
        <address addressId="1">350 5th Avenue</address>
    </location>
</result>

Such that the addressId reflects the sequence of address across all location instances with the same id attribute.

I was thinking <xsl:number> would be my answer, but my attempts have failed:

<?xml version="1.0" encoding="UTF-8"?>
<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:template match="/root">
        <result>
            <xsl:for-each select="location">
                <location>
                    <xsl:attribute name="id">
                        <xsl:value-of select="@id" />
                    </xsl:attribute>

                    <xsl:for-each select="address">
                        <address>
                            <xsl:attribute name="addressId">
                                <xsl:number count="//location[@id = ../@id]/address" level="any" />
                            </xsl:attribute>

                            <!--                            
                              The rest are just my debugging attempts; 
                                curiously addressId3 and addressId4 return
                                different values?
                              -->

                            <!--
                            <xsl:attribute name="addressId2">
                                <xsl:number count="//location[@id = parent::location/@id]/address" level="any" />
                            </xsl:attribute>

                            <xsl:attribute name="addressId3">
                                <xsl:value-of select="count(//location[@id=../@id]/address)" />
                            </xsl:attribute>

                            <xsl:variable name="locId">
                                <xsl:value-of select="../@id" />
                            </xsl:variable>

                            <xsl:attribute name="addressId4">
                                <xsl:value-of select="count(//location[@id=$locId]/address)" />
                            </xsl:attribute>

                            <xsl:attribute name="addressId5">
                                <xsl:number count="//location[@id = '1']/address" level="any" />
                            </xsl:attribute>
                              -->

                            <xsl:value-of select="." />
                        </address>
                    </xsl:for-each>
                </location>
            </xsl:for-each>
        </result>
    </xsl:template>
</xsl:stylesheet>
like image 833
Matt Felzani Avatar asked Feb 15 '23 06:02

Matt Felzani


1 Answers

Here is one way of solving, by counting simply the preceding address elements having a location parent with the same id attribute:

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

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

<xsl:template match="location/address">
  <xsl:copy>
    <xsl:attribute name="addressId">
      <xsl:value-of select="count(preceding::address[../@id = current()/../@id]) + 1"/>
    </xsl:attribute>
    <xsl:apply-templates select="@* | node()"/>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>
like image 164
Martin Honnen Avatar answered Feb 24 '23 15:02

Martin Honnen