Re: [xsl] Merging XML data from different files

Subject: Re: [xsl] Merging XML data from different files
From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx>
Date: Mon, 22 Jan 2001 22:30:13 +0000
Hi Matt,

> I have multiple XML files, and I am able to combine the primary and
> the secondary files according to an 'ID' in the first. There are
> extra 'IDs' in the secondary files that I wish to join as well - How
> do I go about this??

The elements in the secondary document(s) that do not have an
equivalent element in the primary document are those for which it's
not the case that their 'id' attribute (or whatever you've called your
attribute) is the 'id' attribute of an element in the primary
document.

So, let's say that you've set the primary and secondary document root
nodes as $doc1 and $doc2 with:

<xsl:variable name="doc1" select="/" />
<xsl:variable name="doc2" select="document('secondary.xml')" />

You can get all the 'id' attributes in $doc1 with:

  $doc1//@id

It may be worth setting up a variable to hold that list:

  <xsl:variable name="doc1IDs" select="$doc1//@id" />

You can get any elements in the secondary document with:

  $doc2//*

You can get those whose 'id' attribute equals the 'id' attribute of
an element in $doc1 with:

  $doc2//*[@id = $doc1IDs]

So you can get those whose 'id' attribute *does not* equal the 'id'
attribute of any element in $doc1 with:

  $doc2//*[not(@id = $doc1IDs)]

So to add them to your result tree, you can just copy those nodes:

  <xsl:copy-of select="$doc2//*[not(@id = $doc1IDs)]" />

If you have really big documents with lots of IDs, then it might be
worth using keys rather than a path like the above (or using id() if
you have a DTD and the relevant attribute is defined as an ID
attribute); if you use keys, then it makes things a little harder
because of the way the scoping of keys works: they only retrieve nodes
indexed by the key within the document holding the context node. The
key would index all elements according to their 'id' attribute:

<xsl:key name="elements" match="*" use="@id" />

So to get a list of them, you have to iterate over the elements in the
secondary document (using xsl:for-each here for simplicity, but you
could use xsl:apply-templates instead if you wanted):

  <xsl:for-each select="$doc2//*">
     ...
  </xsl:for-each>

You then need to record the element in the secondary document:

  <xsl:for-each select="$doc2//*">
     <xsl:variable name="el2" select="." />
     ...
  </xsl:for-each>

before changing the current node to the primary document:

  <xsl:for-each select="$doc2//*">
     <xsl:variable name="el2" select="." />
     <xsl:for-each select="$doc1">
        ...
     </xsl:for-each>
  </xsl:for-each>

so that you can retrieve nodes using the key you've set up, test
whether there is one, and if not then copy (or whatever) the element
in the secondary document:

  <xsl:for-each select="$doc2//*">
     <xsl:variable name="el2" select="." />
     <xsl:for-each select="$doc1">
        <xsl:if test="not(key('elements', $el2/@id))">
           <xsl:copy-of select="$el2" />
        </xsl:if>
     </xsl:for-each>
  </xsl:for-each>

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