[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message] Re: cross document id idref pair checks
David Carlisle wrote: > Dave Pawson wrote: >> When using well formed, as apposed to valid, input, How to use the >> fact that a node-set removes duplicates to check if there is more >> than one attribute named 'id'? > > If it has duplicated attributes, it's not well formed. I think (?) that DaveP meant duplicated values for @id attributes across a document rather than duplicate @id attributes on a particular element. If there is an element that has the same @id attribute as the current element, then: @id = (preceding::*/@id | following::*/@id) would be true. You can get elements that have repeated ids with: //*[@id = (preceding::*/@id | following::*/@id)] Of course if you test the above expression as a boolean, then it will return true if there are any repeated ids in the document. You can list the IDs that are repeated (once per ID) with: //*[not(preceding::*/@id = @id) and following::*/@id = @id] The preceding:: and following:: axes are, of course, horribly inefficient. You could use a recursive solution instead, or you could use keys: <xsl:key name="ids" select="*" use="@id" /> If, for an element, the 'ids' key returns more than one node, then it's a repeated id, so you can test whether there are any repeats with: //*[key('ids', @id)[2]] [Aside: I've started using positional predicates to test whether a node set has more than a certain number of nodes, although perhaps any processor that optimises '$nodes[2]' will also optimise 'count($nodes) > 1'. Any thoughts?] If you want to get a list (without repeats) of the repeated IDs using the key() method, then you can use the usual: //*[key('ids', @id)[2] and count(.|key('ids', @id)[1]) = 1] [or actually, by extension to the above aside: //*[key('ids', @id)[2] and not((.|key('ids', @id)[1])[2])] making identity-testing of nodes even more obscure! :)] Testing this across documents is harder because (a) nodes from different documents aren't related to each other through axes and (b) key() is scoped within the document of the context node. Probably the simplest solution is to create a node set variable holding copies of the nodes from the relevant documents, and then test that as if it were a document on its own: <xsl:variable name="IDed-elements"> <xsl:copy-of select="(/ | document('foo.xml'))//*[@id]" /> </xsl:variable> <xsl:if test="$IDed-elements//*[key('ids', @id)[2]]"> <!-- repeated IDs --> </xsl:if> Alternatively, you could use a recursive method. Cheers, Jeni --- Jeni Tennison http://www.jenitennison.com/ XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
|
PURCHASE STYLUS STUDIO ONLINE TODAY!Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced! Download The World's Best XML IDE!Accelerate XML development with our award-winning XML IDE - Download a free trial today! Subscribe in XML format
|