This is the mail archive of the xsl-list@mulberrytech.com mailing list .


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]