Subject: Re: [xsl] pruning nodes not in xpath list From: "Cliff McBride" <cliff.mcbride@xxxxxxxxxxxxxx> Date: Wed, 28 Feb 2001 17:07:08 -0800 |
Jeni, Thanks a million!!! I'm not sure I would have ever thought of using "count(.|$selected-nodes) = $nselected-nodes" to test if the context node is in the list of selected nodes. Looks like I need to keep reading the books and experimenting. Thanks again, Cliff ----- Original Message ----- From: "Jeni Tennison" <mail@xxxxxxxxxxxxxxxx> To: "Cliff McBride" <cliff.mcbride@xxxxxxxxxxxxxx> Cc: <XSL-List@xxxxxxxxxxxxxxxxxxxxxx> Sent: Wednesday, February 28, 2001 1:38 AM Subject: Re: [xsl] pruning nodes not in xpath list > Hi Cliff, > > > I would like to be able to generate a sytlesheet that will produce > > output documents that exclude all nodes that are not matched by any > > of the xpath expressions. > > The first thing is to work out a stylesheet that does what you want > with the expressions hard-coded into it... > > > From an algorithm point of view, one solution is to create a > > node-set collection of all nodes that should be copied to the output > > document. For each xpath, find the terminal nodes and add them and > > all of their ancestor elements to the node-set collection. After the > > node-set collection is created, visit each node in source tree in > > document order -- if the node exists in the node-set, use > > <xsl:copy/>. This would ensure that each node only gets copied once, > > and in the document-order so the tree is maintained. I cannot figure > > out a way to implement this algorithm using XSLT. This could be done > > with a DOM (Document), but I don't want to have to implement all of > > the code to handle "xpath" expressions. > > OK, so first work out which nodes have been selected by the > expressions: > > <xsl:variable name="selected-nodes" > select="//product/@sku | //product/cost"/> > > Then to work out which nodes should therefore be copied - that's all > those nodes that are selected, plus their ancestors: > > <xsl:variable name="copied-nodes" > select="$selected-nodes | $selected-nodes/ancestor::*"/> > > Now, starting from the top (the document element is always the first > in the list of copied nodes), we want to work through the document. If > the element is in the list of selected nodes, then we just want to > copy it completely. Otherwise, we want to copy the element, copy any > attributes that are in the list of selected nodes, and apply templates > to any of its children that are in the list of nodes to be copied. > > Now, working out whether a node is in a set of nodes involves a bit of > set manipulation - if you count how many nodes there are in the set > that's the union of the node and the node set, and it's the same as > the number of nodes in the node set, then the node has to be in the > node set. So to work out whether the context node is in the set of > $selected-nodes, I can use: > > count(.|$selected-nodes) = count($selected-nodes) > > Since $selected-nodes and $copied-nodes are known up front, I'll > create variables that indicate how many nodes are in each of them: > > <xsl:variable name="nselected-nodes" select="count($selected-nodes)" /> > <xsl:variable name="ncopied-nodes" select="count($copied-nodes)"/> > > So the template looks like: > > <xsl:template match="*"> > <xsl:choose> > <!-- this node is one of the selected nodes --> > <xsl:when test="count(.|$selected-nodes) = $nselected-nodes"> > <xsl:copy-of select="."/> > </xsl:when> > <xsl:otherwise> > <xsl:copy> > <!-- copy attributes that are selected nodes --> > <xsl:copy-of select="@*[count(.|$selected-nodes) = > $nselected-nodes]"/> > <!-- apply templates to nodes that are to be copied --> > <xsl:apply-templates select="node()[count(.|$copied-nodes) = > $ncopied-nodes]"/> > </xsl:copy> > </xsl:otherwise> > </xsl:choose> > </xsl:template> > > And there you have it. That stylesheet will take the input you > specified and give the output you specified. > > Now all you have to do is create a stylesheet that creates it. In > fact most of it is static, so you could bundle it out to a separate > stylesheet and include it rather than create it dynamically. The only > thing that isn't static is the creation of the $selected-nodes > variable: > > <xsl:variable name="selected-nodes" > select="//product/@sku | //product/cost"/> > > Assuming that you're using oxsl as the namespace prefix for your XSLT > namespace alias, then you need to create an oxsl:variable with a name > attribute equal to selected-nodes: > > <oxsl:variable name="selected-nodes"> > ... > </oxsl:variable> > > The select attribute is created dynamically by iterating over the > various expressions that you have (I'll assume their held in an $exprs > variable): > > <oxsl:variable name="selected-nodes"> > <xsl:attribute name="select"> > <xsl:for-each select="$exprs"> > <xsl:value-of select="." /> > <xsl:if test="position() != last()"> | </xsl:if> > </xsl:for-each> > </xsl:attribute> > </oxsl:variable> > > You should be able to create the rest of the stylesheet very > straight-forwardly. > > I hope that helps, > > Jeni > > --- > Jeni Tennison > http://www.jenitennison.com/ > > XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
Re: [xsl] pruning nodes not in xpat, Jeni Tennison | Thread | [xsl] <![CDATA[ in Sablotron Parser, Tim Watts |
[xsl] Counting Colums in WML, Ciaran Byrne | Date | RE: [xsl] Is XML to CHTML Transform, Peter Flynn |
Month |