Descendent's Relative position neede when flattening XSL

D

delgados129

In a previous post (XSL to Flatten Selective Node in XML:
http://groups-beta.google.com/group...19953e7?q=delgados129&rnum=1#16104948219953e7)
I was skillfully pointed in the right direction with XSL logic to
flatten a selected node.

Given:

<A>
<B/>
<C>
<D>DText1</D>
<D>DText2</D>
<D>
<E>EText1</E>
</D>
<D>DText3</D>
<D>
<E>EText2</E>
</D>
</C>
</A>

Tranforming the XML with the following XSL:

<?xml version="1.0" encoding="UTF-16"?>
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:eek:utput method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="C">
<xsl:apply-templates mode="flatten">
<xsl:with-param name="currentNode" select="'C'"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="*" mode="flatten">
<xsl:param name="currentNode"/>
<xsl:variable name="delimitter" select="'-'" />
<xsl:if test="text()">
<xsl:element name="{$currentNode}{$delimitter}{name()}">
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:if>
<xsl:apply-templates select="*" mode="flatten">
<xsl:with-param name="currentNode"
select="concat($currentNode,$delimitter,name())"/>
</xsl:apply-templates>
</xsl:template>
</xsl:transform>

Will yield:

<A>
<B/>
<C-D>DText1</C-D>
<C-D>DText2</C-D>
<C-D-E>EText1</C-D-E>
<C-D>DText3</C-D>
<C-D-E>EText2</C-D-E>
</A>

Any suggestions on enhancing the transformation to yield the
grandchildren's (great grandchild, great-great grandchild, etc.)
relative element name count. (Note that text 'DText3' within its parent
node is not necessarily an immediate sequential sibling to its logical
sibling.) This is only necessary, as noted in the XSL, for terminating
elements with text. To be more specific, the transform should then
yield:

<A>
<B/>
<C-D-1>DText1</C-D-1>
<C-D-2>DText2</C-D-2>
<C-D-E-1>EText1</C-D-E-1>
<C-D-3>DText3</C-D-3>
<C-D-E-2>EText2</C-D-E-2>
</A>

BTW:

A heartfelt thanks to all that contribute to making this group so
helpful. I enjoy reading through many of the solutions provided here.
 
D

David Carlisle

<xsl:if test="text()">
<xsl:variable name="this" select="name()"/>
<xsl:variable name="n">
<xsl:number level="any" count="*[name()=$this][text()]"/>
</xsl:variable>
<xsl:element name="{$x}_{name()}_{$n}">
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:if>
 
D

delgados129

For documentation completeness, I've incorporated David's logic to
produce the following XSL, also showing its input and final
transformation. Once again, thanks David!

XML Input:

<A>
<B/>
<C>
<D>DText1</D>
<D>DText2</D>
<D>
<E>EText1</E>
</D>
<D>DText3</D>
<D>
<E>EText2</E>
</D>
</C>
</A>



XSL Transform:

<?xml version="1.0" encoding="UTF-16"?>
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:eek:utput method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>

<!-- Match value is element node to apply flattening logic at -->
<xsl:template match="C">
<xsl:apply-templates mode="flatten">
<xsl:with-param name="currentNode" select="name()"/>
</xsl:apply-templates>
</xsl:template>

<xsl:template match="*" mode="flatten">
<xsl:param name="currentNode"/>
<xsl:variable name="delimitter" select="'-'"/>
<xsl:variable name="thisNode" select="name()"/>
<xsl:if test="text()">
<xsl:variable name="descendentNumber">
<xsl:number level="any" count="*[name()=$thisNode][text()]"/>
</xsl:variable>
<xsl:element
name="{$currentNode}{$delimitter}{$thisNode}{$delimitter}{$descendentNumber}">
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:if>
<xsl:apply-templates select="*" mode="flatten">
<xsl:with-param name="currentNode"
select="concat($currentNode,$delimitter,$thisNode)"/>
</xsl:apply-templates>
</xsl:template>

</xsl:transform>

XML Trasformed to:

<?xml version="1.0" encoding="UTF-8"?>
<A>
<B/>
<C-D-1>DText1</C-D-1>
<C-D-2>DText2</C-D-2>
<C-D-E-1>EText1</C-D-E-1>
<C-D-3>DText3</C-D-3>
<C-D-E-2>EText2</C-D-E-2>
</A>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top