RE: [xsl] XSLT Filtering based on defined XML

Subject: RE: [xsl] XSLT Filtering based on defined XML
From: "Michael Kay" <mike@xxxxxxxxxxxx>
Date: Wed, 17 Aug 2005 00:37:11 +0100
OK, I' having to make a few guesses about what you want to happen to text
nodes etc, but it seems to be something like this:

Each node N of the input document has a corresponding node F in the filter
document.

The root node of the of the input document corresponds to the root node of
the filter document.

If F has no element children, then N is deep-copied unchanged to the result
tree.

If F has one or more element children, then N is shallow-copied to the
result tree. Those children of N where a child of F exists with the same
name are handled recursively using the same rules, using that element of F
as the corresponding node; those children of N where no child of F exists
with the same name are omitted from the output.

OK?

(There doesn't seem to be any mechanism for shallow-copying N without any of
its children: the rule "no children in the filter means copy all children
from the input" seems a slighly odd one).



This suggests processing something like this (2.0 solution):

<xsl:variable name="in" select="/"/>
<xsl:variable name="filter" select="doc('filter.xml')">

<xsl:template match="/">
  <xsl:apply-templates select="*">
    <xsl:with-param name="f" select="$filter/*"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="*">
  <xsl:param name="f"/>
  <xsl:choose>
  <xsl:when test="$f/*">
    <xsl:for-each select="*[node-name() = $f/*/node-name()]">
      <xsl:apply-templates select=".">
        <xsl:with-param name="f" select="f/*[node-name() =
current()/node-name()]"/>
      </xsl:apply-templates>
    </xsl:for-each>
  </xsl:when>
  <xsl:otherwise>
    <xsl:copy-of select="."/>
  </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Untested, of course.

Michael Kay
http://www.saxonica.com/

> -----Original Message-----
> From: Jacquo Johnson [mailto:genxgeek@xxxxxxxxx] 
> Sent: 17 August 2005 00:09
> To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
> Subject: Re: [xsl] XSLT Filtering based on defined XML
> 
> Actually, the nice thing about the requirements in this case is that
> the source document will always be the same structure.  That is, it is
> well defined other than the filters, which can change but only
> according to the xml source document definition (just the xml
> structure...not refering to a dtd here);:
> 
> Let's say the the following is the actual xml source contents 
> (always static):
> 
> <Root>
>   <Node1/>
>   <Node2>
>      <Node21/>
>      <Node22/>
>  </Node2>
> </Root>
> 
> Now the only available filters for this document would be:
> 
> Filter 1:
> <Root>
>   <Node1/>
> </Root>
> 
> Output (from Filter1):
> <Root>
>   <Node1/>
> </Root>
> --
> Filter 2:
> <Root>
>   <Node2>
>      <Node21/>
>  </Node2>
> </Root>
> 
> Output (from Filter2):
> <Root>
>   <Node2>
>      <Node21/>
>  </Node2>
> </Root>
> 
> Filter 3:
> <Root>
>   <Node2>
>      <Node22/>
>  </Node2>
> </Root>
> 
> Output (from Filter3):
> <Root>
>   <Node2>
>      <Node22/>
>  </Node2>
> </Root>
> 
> Filter 4:
> <Root>
>   <Node2/>
> </Root>
> 
> Output (from Filter2):
> <Root>
>   <Node2>
>      <Node21/>
>      <Node22/>
>  </Node2>
> </Root>
> 
> Now, since I have a DOM object available I can dynamically create my
> own xslt by recursing the filter and create <apply-templates
> match="<CurrentNodeFromFilter>"/> and either <copy> if the node
> doesn't have children or do a <copy-of> if the node has children. 
> Once I have the dynamic transform (as created per the filter) I can
> call a transform with it on the xml source document to get me the
> desired output.  This works but I think that it's kludgey.
> 
> ----
> On 8/16/05, Michael Kay <mike@xxxxxxxxxxxx> wrote:
> > Your first challenge is to specify this more precisely.
> > 
> > In early drafts of the XQuery specification there was a 
> function called
> > filter() which was a bit like this, but it got taken out 
> because no-one was
> > able to write a precise specification of what it was supposed to do.
> > Sketching out an example isn't good enough. Start by 
> producing a dozen
> > examples: For example, what output do you want if one of 
> the documents is
> > 
> > <a><b/></a>
> > 
> > and the other is
> > 
> > <b><c/></b>
> > 
> > then generalize from the examples to define the actual rules.
> > 
> > Michael Kay
> > 
> > > -----Original Message-----
> > > From: Jacquo Johnson [mailto:genxgeek@xxxxxxxxx]
> > > Sent: 16 August 2005 20:26
> > > To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
> > > Subject: [xsl] XSLT Filtering based on defined XML
> > >
> > > Hi All,
> > >
> > > Is there an elegant pattern via xslt to filter out an 
> existing source
> > > xml document based on the contents of another filter xml document?
> > > For example:
> > >
> > > Source xml doc:
> > > <Root>
> > >    <Node1>
> > >       <Node11>
> > >         <Node111/>
> > >       </Node11>
> > >       <Node12/>
> > >    <Node2>
> > >    ...
> > >
> > > Filter xml doc:
> > > <Root>
> > >    <Node1>
> > >      <Node111/>
> > >    </Node1>
> > > </Root>
> > >
> > > Desired results:
> > > <Root>
> > >    <Node1>
> > >      <Node111/>
> > >    </Node1>
> > > </Root>
> > >
> > > Now the issue that I'm dealing with is that the filter xml doc is
> > > dynamic.  That is, it can be different for any given xml 
> source doc
> > > configuration based on what another server source 
> specifies.  I could
> > > recursively build some dynamic xslt and add 
> apply-templates for each
> > > node and make use of copy and copy-of based on if a node 
> has children
> > > or not and then run that dynamically build transform on 
> the xml doc
> > > source...however, can anybody think of another way to do this?
> > >
> > > Thanks in advance!

Current Thread