Reputation: 73
I have a single flat list of "users" with parent-child relationships to other "users". A user's "parent" is the approverID. I need to sort this list so that no children ever precede their parents in the list, can use XSLT 2.0. Example input xml:
<userList>
<user>
<userID>4</userID>
<approverID>2</approverID>
</user>
<user>
<userID>5</userID>
<approverID>2</approverID>
</user>
<user>
<userID>3</userID>
<approverID>1</approverID>
</user>
<user>
<userID>2</userID>
<approverID>1</approverID>
</user>
<user>
<userID>1</userID>
<approverID>10</approverID>
</user>
<user>
<userID>6</userID>
<approverID>7</approverID>
</user>
<user>
<userID>7</userID>
<approverID>10</approverID>
</user>
</userList>
Would have a parent child structure like (not sure best way to show it)
1 { 2 {4,5} , 3}
7 {6 }
And output XML could look something like
<userList>
<user>
<userID>1</userID>
<approverID>10</userID>
</user>
<user>
<userID>2</userID>
<approverID>1</approverID>
</user>
<user>
<userID>3</userID>
<approverID>1</approverID>
</user>
<user>
<userID>4</userID>
<approverID>2</approverID>
</user>
<user>
<userID>5</userID>
<approverID>2</approverID>
</user>
<user>
<userID>7</userID>
<approverID>10</approverID>
</user>
<user>
<userID>6</userID>
<approverID>7</approverID>
</user>
</userList>
The only requirement is that a child user never comes before the parent, but other than that it can be sorted anyway. I feel I could do this recursively, but I know that's not the best option in a functional programming language like XSLT.
Upvotes: 1
Views: 148
Reputation: 101748
There seem to be some inconsistencies between your input and output (and I think your output violates your requirements in two places), but I think this is what you are going for:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:key name="kUserByApprover" match="user" use="approverID"/>
<xsl:key name="kUserById" match="user" use="userID"/>
<xsl:template match="@* | node()" name="copy">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="userList">
<xsl:copy>
<!-- Process users whose approver is not present -->
<xsl:apply-templates select="user[not(key('kUserById', approverID))]" />
</xsl:copy>
</xsl:template>
<xsl:template match="user">
<xsl:call-template name="copy" />
<!-- Process child users -->
<xsl:apply-templates select="key('kUserByApprover', userID)" />
</xsl:template>
</xsl:stylesheet>
When run on your sample input, the result is:
<userList>
<user>
<userID>1</userID>
<approverID>10</approverID>
</user>
<user>
<userID>3</userID>
<approverID>1</approverID>
</user>
<user>
<userID>2</userID>
<approverID>1</approverID>
</user>
<user>
<userID>4</userID>
<approverID>2</approverID>
</user>
<user>
<userID>5</userID>
<approverID>2</approverID>
</user>
<user>
<userID>7</userID>
<approverID>10</approverID>
</user>
<user>
<userID>6</userID>
<approverID>7</approverID>
</user>
</userList>
Upvotes: 1