Re: [xsl] Identifying two tags that share some attribute names and values

Subject: Re: [xsl] Identifying two tags that share some attribute names and values
From: Zack Brown <zbrown@xxxxxxxxxxxxxxx>
Date: Sun, 5 May 2002 22:03:47 -0700
Hi,

There's still a problem. when this recipe compares attributes, it sees
line-breaks within an attribute and uses them in its comparison. So <a x="hi
there"/> won't match <b x="hi there"/>.

Is there any way around this?

On Sun, May 05, 2002 at 10:08:50AM -0700, Zack Brown wrote:
> OUCH!
> 
> I apologize for this, but it looks like I slightly misstated the problem. I
> tested your solution and it looks like exactly what I'm looking for, with one
> difference: it is not <a> that contains the superset of attributes, with <b>
> containing a subset. It's actually <b> that contains the superset, and <a>
> contains the subset.
> 
> This variation seems to work for me:
> 
> <xsl:variable name="file2" select="document('2.xml')/outsidedata"/>
> <xsl:template match="a">
>   <xsl:variable name="a" select="." />
>   <xsl:for-each select="$file2/b[@* = $a/@*]">
>     <xsl:variable name="b" select="." />
>     <xsl:variable name="test">
>       <xsl:for-each select="$a/@*">
>         <xsl:if test="not($b/@*[name() = name(current())] = .)">
>           no attribute with the same name on element b whose value equals this one
>         </xsl:if>
>       </xsl:for-each>
>     </xsl:variable>
>     <xsl:if test="not(string($test))">
>       [<xsl:value-of select="."/>]
>     </xsl:if>
>   </xsl:for-each>
> </xsl:template>
> 
> Thanks!!
> 
> Zack
> 
> On Sun, May 05, 2002 at 11:01:25AM +0100, Jeni Tennison wrote:
> > Hi Zack,
> > 
> > >> I didn't get it with one expression and need the step with $test:
> > >> 
> > >> <xsl:variable name="file1" select="/"/>
> > >> <xsl:variable name="file2" select="document('2.xml')/outsidedata"/>
> > >> 
> > >> <xsl:template match="/">
> > >>     <xsl:apply-templates select="$file2/b"/>
> > >
> > > That's a problem, because 2.xml may contain thousands of entries,
> > > and I don't want to process each one. I just want to process the <a>
> > > from 1.xml and find any corresponding <b> in 2.xml.
> > 
> > To search for a b element in 2.xml that matches your criteria, you're
> > going to have to process each of them in some way. I agree that's
> > going to be time-consuming given you have thousands of entries. There
> > are things that you could do to make it easier to search 2.xml --
> > rearrange the XML so that the bs are grouped by which attributes they
> > have, for example. Or carry out some filtering on 2.xml prior to using
> > it with XSLT.
> > 
> > If you prefer to think of it as processing the a element, then have a
> > template that matches the a element, then you could do so:
> > 
> > <xsl:template match="a">
> >   <xsl:variable name="a" select="." />
> >   <xsl:for-each select="$file2/b[@* = $a/@*]">
> >     <xsl:variable name="test">
> >       <xsl:for-each select="@*">
> >         <xsl:if test="not($a/@*[name() = name(current())] = .)">
> >           no attribute with the same name on element a whose value
> >           equals this one
> >         </xsl:if>
> >       </xsl:for-each>
> >     </xsl:variable>
> >     <xsl:if test="not(string($test))">
> >       <xsl:text />[<xsl:value-of select="." />]<xsl:text />
> >     </xsl:if>
> >   </xsl:for-each>
> > </xsl:template>
> > 
> > Three minor variations on Joerg's solution here. First, I filter the b
> > elements that you iterate over to only those that have an attribute
> > whose value matches one of the attributes on a. That's just a rough
> > filter, and it won't lower the number of visits to b elements overall,
> > but it will prevent you from building up the $test variable for every
> > one of them.
> > 
> > Second, the test in the middle is a bit simpler. Joerg used:
> > 
> >   $file1/a/@*[name()=name(current())][. != current()] or
> >   not($file1/a/@*[name()=name(current())])
> > 
> > which returns true if there is an attribute on a with the same name as
> > the attribute on b that you're looking at whose value is not equal to
> > the attribute on b *or* there's no attribute on a with the same name
> > as the attribute on b that you're looking at.
> > 
> > The one I've used above is:
> > 
> >   not($a/@*[name() = name(current())] = .)
> > 
> > which returns true if it is not the case that there is an attribute on
> > a, with the same name as the attribute on b that you're looking at,
> > whose value is the same as the attribute on b. This will therefore
> > return true either if the attribute on a with the same name isn't
> > equal to the value of the attribute on b, or if there's no attribute
> > on a with the same name.
> > 
> > The second slight difference is in the value created for the $test
> > variable. All that you need to test here is whether $test ends up
> > having some content or not -- the only way it can have content is if
> > the test were true for one of the attributes, so you can use
> > test="not(string($test))" (does the $test variable not have a string
> > value?). Because of this, I tend to use something meaningful as the
> > string within the test -- here "no attribute with the same name on
> > element a whose value equals this one" because it documents what
> > you're doing a little and is helpful if you had to debug the code --
> > you could print out the value of $test and get a meaningful message.
> >   
> > Cheers,
> > 
> > Jeni
> > 
> > ---
> > Jeni Tennison
> > http://www.jenitennison.com/
> > 
> > 
> >  XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
> > 
> 
> -- 
> Zack Brown
> 
>  XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
> 

-- 
Zack Brown

 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread