XSLT Recursive Reversal of Ancestry

A

alex.maddern

I have an XML file containing a hierarchical relationship between
"Object Types". Each relationship contains a reference to the GUID for
that Object and I have been tasked to extract these GUID's in reverse
order into a pipe delimited file. The file will be used to delete
these objects using the GUID as an ID, so the children need to be
processed first

======================
Sample Object Type Hierarchy
======================
Not all files will be like this obviously

TYPE1 <-- only one of Type1
TYPE2
TYPE3
TYPE4
TYPE2
TYPE3
TYPE4
TYPE4
TYPE3
TYPE4
TYPE5
TYPE6
TYPE6
TYPE6
TYPE4
TYPE5
TYPE6
TYPE2
TYPE3
TYPE4

What I am trying to achieve (given the above example) is a text file
as follows the follows.I am pretty sure that having all the Guid's in
one long Pipe delimited line is fine, I'm just splitting them up to
show you the principle.

[Type4Guid] | [Type3Guid] | [Type2Guid]
[Type4Guid] | [Type4Guid] | [Type3Guid]
[Type6Guid] | [Type6Guid] | [Type6Guid] | [Type5Guid] | [Type4Guid]
[Type6Guid] | [Type5Guid] | [Type4Guid] | [Type3Guid] | [Type2Guid]
[Type4Guid] | [Type3Guid] | [Type2Guid]
[Type1Guid]

Before anyone asks, I have no control over this and this is frankly
the last way I would do all this, but I have no choice in matter. To
be honest this smells of recursive pivoting on ancestor-or-self axis
with position limits, but it's way over my level of skill with XSLT.

Any guru's out there with a direction to point me in ? All help
seriously welcomed...

Samples as follows

===========
INPUT XML
===========

<?xml version="1.0" encoding="utf-8"?>
<AssociationTreeNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-
instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<guid>060A2B340101010501010D12130C3EDFE6A4470035430580BD8F000000000000</
guid>
<objectID>78c790d6-99d8-4a65-ab39-8f660c4082d4</objectID>
<children>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130C3DE784AA47003543058084CF000000000000</
guid>
<objectID>f052b382-b246-4267-9a00-cd79a95fd9f7</objectID>
<parentAssociationType>TYPE1_TYPE2</parentAssociationType>
<children>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130C00ED82324D00354305808C6D000000000000</
guid>
<objectID>75c95e90-8afc-4149-9f3b-135447e42301</objectID>
<parentAssociationType>TYPE2_TYPE3</parentAssociationType>
<children>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130C5E7010384D00354305805875000000000000</
guid>
<objectID>32f05cef-9a01-4ff6-aaf4-359b33a738a2</
objectID>
<parentAssociationType>TYPE3_TYPE4</
parentAssociationType>
<children />
</AssociationTreeNode>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130C1476E2804D0035430580407F000000000000</
guid>
<objectID>32f05cef-9a01-4ff6-aaf4-359b323348a2</
objectID>
<parentAssociationType>TYPE3_TYPE4</
parentAssociationType>
<children />
</AssociationTreeNode>
</children>
</AssociationTreeNode>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130C02FC4405DD0035430580AD6B000000000000</
guid>
<objectID>75c95e90-8afc-4149-9f3b-135447e42301</objectID>
<parentAssociationType>TYPE2_TYPE3</parentAssociationType>
<children>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130CB5B0200BDD003543058017C7000000000000</
guid>
<objectID>32f05cef-9a01-4ff6-aaf4-359b33a738a2</
objectID>
<parentAssociationType>TYPE3_TYPE4</
parentAssociationType>
<children />
</AssociationTreeNode>
</children>
</AssociationTreeNode>
</children>
</AssociationTreeNode>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130C417052F88E00354305808CAB000000000000</
guid>
<objectID>f052b382-b246-4267-9a00-cd79a95fd9f7</objectID>
<parentAssociationType>TYPE1_TYPE2</parentAssociationType>
<children>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130C124BE667EB003543058033B7000000000000</
guid>
<objectID>75c95e90-8afc-4149-9f3b-135447e42301</objectID>
<parentAssociationType>TYPE2_TYPE3</parentAssociationType>
<children>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130CD6FBA26DEB00354305805FE8000000000000</
guid>
<objectID>32f05cef-9a01-4ff6-aaf4-359b33a738a2</
objectID>
<parentAssociationType>TYPE3_TYPE4</
parentAssociationType>
<children />
</AssociationTreeNode>
</children>
</AssociationTreeNode>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130C29277497AD00354305808CF5000000000000</
guid>
<objectID>75c95e90-8afc-4149-9f3b-135447e42301</objectID>
<parentAssociationType>TYPE2_TYPE3</parentAssociationType>
<children>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130CE9EC7F9DAD00354305801FC2000000000000</
guid>
<objectID>32f05cef-9a01-4ff6-aaf4-359b33a738a2</
objectID>
<parentAssociationType>TYPE3_TYPE4</
parentAssociationType>
<children>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130C2C627CC0D00455430580053C000000000000</
guid>
<objectID>9214702c-0d18-40fc-b4e7-ab1ee37e7b05</
objectID>
<parentAssociationType>TYPE4_TYPE5</
parentAssociationType>
<children>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130C3DDA3C886B0335430580B38B000000000000</
guid>
<objectID>2ce8c725-dea1-4aeb-9a07-f988260e5180</
objectID>
<parentAssociationType>TYPE5_TYPE6</
parentAssociationType>
<children />
</AssociationTreeNode>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130C4277C9886B03354305801240000000000000</
guid>
<objectID>2ce8c725-dea1-4aeb-9a07-f988260e5180</
objectID>
<parentAssociationType>TYPE5_TYPE6</
parentAssociationType>
<children />
</AssociationTreeNode>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130C44A56B886B03354305808412000000000000</
guid>
<objectID>2ce8c725-dea1-4aeb-9a07-f988260e5180</
objectID>
<parentAssociationType>TYPE5_TYPE6</
parentAssociationType>
<children />
</AssociationTreeNode>
</children>
</AssociationTreeNode>
</children>
</AssociationTreeNode>
</children>
</AssociationTreeNode>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130C2F30189ED2003543058082BA000000000000</
guid>
<objectID>75c95e90-8afc-4149-9f3b-135447e42301</objectID>
<parentAssociationType>TYPE2_TYPE3</parentAssociationType>
<children>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130C22D7A5A3D200354305808197000000000000</
guid>
<objectID>32f05cef-9a01-4ff6-aaf4-359b33a738a2</
objectID>
<parentAssociationType>TYPE3_TYPE4</
parentAssociationType>
<children>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130C28D3674A3400614305805010000000000000</
guid>
<objectID>9214702c-0d18-40fc-b4e7-ab1ee37e7b05</
objectID>
<parentAssociationType>TYPE4_TYPE5</
parentAssociationType>
<children>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130C4F14E2B75A0455430580F244000000000000</
guid>
<objectID>2ce8c725-dea1-4aeb-9a07-f988260e5180</
objectID>
<parentAssociationType>TYPE5_TYPE6</
parentAssociationType>
<children />
</AssociationTreeNode>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130C50EEB3B75A04554305804078000000000000</
guid>
<objectID>2ce8c725-dea1-4aeb-9a07-f988260e5180</
objectID>
<parentAssociationType>TYPE5_TYPE6</
parentAssociationType>
<children />
</AssociationTreeNode>
</children>
</AssociationTreeNode>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130C86572CF8BD03824305801925000000000000</
guid>
<objectID>9214702c-0d18-40fc-b4e7-ab1ee37e7b05</
objectID>
<parentAssociationType>TYPE4_TYPE5</
parentAssociationType>
<children>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130C1FF7C797BE03824305806D3E000000000000</
guid>
<objectID>2ce8c725-dea1-4aeb-9a07-f988260e5180</
objectID>
<parentAssociationType>TYPE5_TYPE6</
parentAssociationType>
<children />
</AssociationTreeNode>
</children>
</AssociationTreeNode>
</children>
</AssociationTreeNode>
</children>
</AssociationTreeNode>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130C9580C9EB0A013543058079D6000000000000</
guid>
<objectID>75c95e90-8afc-4149-9f3b-135447e42301</objectID>
<parentAssociationType>TYPE2_TYPE3</parentAssociationType>
<children>
<AssociationTreeNode>

<guid>060A2B340101010501010D12130CBA4FA5F10A0135430580FE5A000000000000</
guid>
<objectID>32f05cef-9a01-4ff6-aaf4-359b33a738a2</
objectID>
<parentAssociationType>TYPE3_TYPE4</
parentAssociationType>
<children />
</AssociationTreeNode>
</children>
</AssociationTreeNode>
</children>
</AssociationTreeNode>
</children>
</AssociationTreeNode>
 
P

Pavel Lepin

I have an XML file containing a hierarchical relationship
between "Object Types". Each relationship contains a
reference to the GUID for that Object and I have been
tasked to extract these GUID's in reverse order into a
pipe delimited file. The file will be used to delete these
objects using the GUID as an ID, so the children need to
be processed first

What I am trying to achieve (given the above example) is a
text file as follows the follows.I am pretty sure that
having all the Guid's in one long Pipe delimited line is
fine, I'm just splitting them up to show you the
principle.

[Type4Guid] | [Type3Guid] | [Type2Guid]
[Type4Guid] | [Type4Guid] | [Type3Guid]
[Type6Guid] | [Type6Guid] | [Type6Guid] | [Type5Guid] |
[[Type4Guid] Type6Guid] | [Type5Guid] | [Type4Guid] |
[[Type3Guid] | [Type2Guid] Type4Guid] | [Type3Guid] |
[[Type2Guid] Type1Guid]

[sample document snipped]

First of all, it would've been nice of you to replace the
actual guids with something intelligible for the purposes
of testing. For example, the following transformation does
just that:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="l" match="guid"
use="count(ancestor::AssociationTreeNode)"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="guid">
<xsl:variable name="level"
select="count(ancestor::AssociationTreeNode)"/>
<xsl:variable name="number"
select=
"
1 +
count
(
key('l' , count(ancestor::AssociationTreeNode))
[
following::guid
[generate-id()=generate-id(current())]
]
)
"/>
<xsl:copy>
<xsl:text>Type-</xsl:text>
<xsl:value-of select="$level"/>
<xsl:text>-Guid-</xsl:text>
<xsl:value-of select="$number"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

The problem itself seems absolutely trivial to me:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:eek:utput method="text"/>
<xsl:template match="AssociationTreeNode">
<xsl:apply-templates
select="children/AssociationTreeNode">
<xsl:sort select="position()" order="descending"/>
</xsl:apply-templates>
<xsl:text>[</xsl:text>
<xsl:apply-templates select="guid"/>
<xsl:text>] | </xsl:text>
</xsl:template>
</xsl:stylesheet>

....seems to achieve what you want.

pavel@debian:~/dev/xslt$ saxon -t guids_1.xml guids.xsl
Saxon 8.8J from Saxonica
Java version 1.5.0_11
Warning: at xsl:stylesheet on line 2 of
file:/var/www/dev/xslt/guids.xsl:
Running an XSLT 1.0 stylesheet with an XSLT 2.0 processor
Stylesheet compilation time: 504 milliseconds
Processing file:/var/www/dev/xslt/guids_1.xml
Building tree for file:/var/www/dev/xslt/guids_1.xml using
class net.sf.saxon.tinytree.TinyBuilder
Tree built in 45 milliseconds
Tree size: 362 nodes, 3544 characters, 0 attributes
[Type-4-Guid-7] | [Type-3-Guid-6] | [Type-6-Guid-6] |
[Type-5-Guid-3] | [Type-6-Guid-5] | [Type-6-Guid-4] |
[Type-5-Guid-2] | [Type-4-Guid-6] | [Type-3-Guid-5] |
[Type-6-Guid-3] | [Type-6-Guid-2] | [Type-6-Guid-1] |
[Type-5-Guid-1] | [Type-4-Guid-5] | [Type-3-Guid-4] |
[Type-4-Guid-4] | [Type-3-Guid-3] | [Type-2-Guid-2] |
[Type-4-Guid-3] | [Type-3-Guid-2] | [Type-4-Guid-2] |
[Type-4-Guid-1] | [Type-3-Guid-1] | [Type-2-Guid-1] |
[Type-1-Guid-1] | Execution time: 140 milliseconds
Memory used: 609944
NamePool contents: 18 entries in 18 chains. 8 prefixes, 8
URIs
pavel@debian:~/dev/xslt$
 
A

alex.maddern

First of all, it would've been nice of you to replace the
actual guids with something intelligible for the purposes
of testing. For example, the following transformation does
just that:

That is really neat and will help my testing a lot, thanks

The problem itself seems absolutely trivial to me:

ermmm OK, but perhaps not to people who are not highly skilled in XSL
like yourself ?

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:eek:utput method="text"/>
<xsl:template match="AssociationTreeNode">
<xsl:apply-templates
select="children/AssociationTreeNode">
<xsl:sort select="position()" order="descending"/>
</xsl:apply-templates>
<xsl:text>[</xsl:text>
<xsl:apply-templates select="guid"/>
<xsl:text>] | </xsl:text>
</xsl:template>
</xsl:stylesheet>

...seems to achieve what you want.

Thank you Pavel, that is a great help to me.

If I can only come up with a means to exclude the final | char on the
end, testing for position() last()/first() yields various entries
within the output. I'll have a play in the morning, thanks again!

Al
 
P

Pavel Lepin

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:eek:utput method="text"/>
<xsl:template match="AssociationTreeNode">
<xsl:apply-templates
select="children/AssociationTreeNode">
<xsl:sort select="position()" order="descending"/>
</xsl:apply-templates>
<xsl:text>[</xsl:text>
<xsl:apply-templates select="guid"/>
<xsl:text>] | </xsl:text>
</xsl:template>
</xsl:stylesheet>

If I can only come up with a means to exclude the final |
char on the end, testing for position() last()/first()
yields various entries within the output.

That's because position() returns the context position
within the current node list. To check whether a node is
the topmost node of your hierarchy, you should check its
ancestors:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:eek:utput method="text"/>
<xsl:template match="AssociationTreeNode">
<xsl:apply-templates select="." mode="r"/>
<xsl:text> | </xsl:text>
</xsl:template>
<xsl:template
match=
"
AssociationTreeNode
[not(ancestor::AssociationTreeNode)]
">
<xsl:apply-templates select="." mode="r"/>
</xsl:template>
<xsl:template match="AssociationTreeNode" mode="r">
<xsl:apply-templates
select="children/AssociationTreeNode">
<xsl:sort select="position()" order="descending"/>
</xsl:apply-templates>
<xsl:text>[</xsl:text>
<xsl:apply-templates select="guid"/>
<xsl:text>]</xsl:text>
</xsl:template>
</xsl:stylesheet>
 
A

alex.maddern

That's because position() returns the context position
within the current node list. To check whether a node is
the topmost node of your hierarchy, you should check its
ancestors:

Outstanding Pavel, thank you again. I'm certainly learning a lot from
this.

Al
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,576
Members
45,054
Latest member
LucyCarper

Latest Threads

Top