Merging children from different elements

P

PC Paul

Hi,

I have a problem that seems like it ought to be solvable with a couple
of XPath statements but after a couple of days I can't find them..

Original XML:

<People>
<PartialPerson>
<Name>Brian</Name>
<Nickname>BB</Nickname>
</PartialPerson>
<PartialPerson>
<Name>Geoffrey</Name>
<Email>[email protected]</Email>
</PartialPerson>
<PartialPerson>
<Nickname>BB</Nickname>
<Email>[email protected]</Email>
</PartialPerson>
</People>

What I want to do is collect all of the info for a given person together
by linking equal fields: e.g. to end up with this:

<People>
<CompletePerson>
<Name>Brian</Name>
<Nickname>BB</Nickname>
<Email>[email protected]</Email>
</CompletePerson>
<CompletePerson>
<Name>Geoffrey</Name>
<Email>[email protected]</Email>
</CompletePerson>
</People>

The actual data might have 6+ fields, and I don't know which will be
filled in each time. Each of the fields is guaranteed to have unique
contents within the XML file, so what I need to do is to link together
all of the child elements from any other PartialPerson where any of it's
children match the equivalent child of this one. If you see what I mean.

I've got close by doing a for-each over all of the Name fields and
collecting all others with the same Name ec., but because there is no
field that is always present I end up having to go round again and again
to catch all of the cases, and with 6 child elements to consider it
grows to ridiculous proportions.

Ummm.... Help??
 
I

Ixa

I've got close by doing a for-each over all of the Name fields and
collecting all others with the same Name

How about using <xsl:key> instead of <Name> and just picking up relevant data?

For example:

---8<---8<---
<xsl:key name="person" match="PartialPerson" use="*"/>

<xsl:template match="/People">
<xsl:copy>
<xsl:for-each select="PartialPerson">
<xsl:variable name="identifier" select="*"/>
<xsl:if test="not(preceding-sibling::partialPerson
[key('person', *) = key('person', $identifier)])">
<xsl:apply-templates select="."/>
</xsl:if>
</xsl:for-each>
</xsl:copy>
</xsl:template>

<xsl:template match="PartialPerson">
<CompletePerson>
<xsl:apply-templates select="key('person', *)[Name][1]/Name"/>
<xsl:apply-templates select="key('person', *)[Nickname][1]/Nickname"/>
<xsl:apply-templates select="key('person', *)[1]/Email"/>
</CompletePerson>
</xsl:template>

<xsl:template match="PartialPerson/*">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
---8<---8<---
 
P

PC Paul

Ixa said:
How about using <xsl:key> instead of <Name> and just picking up relevant
data?
<snip>

That works perfectly, thanks. The bit I was missing was having a key
over all of the possible elements instead of just one. I don't think I
think in enough dimensions yet to get the best out of XSL...
<xsl:if test="not(preceding-sibling::partialPerson
[key('person', *) = key('person', $identifier)])">

This bit was interesting - I'm using Oxygen XML to develop the
transforms, and with Saxon 6.5.5 it works fine, but with the included
Xalan (Java version 2.7.0) the line above gives an 'Unknown Error in
XPath' and dies.

With Xalan-C 1.10.0 (which is what I actually need to use), it works
fine. So it's not something that I need to get fixed right now, but I'm
curious what it might be. Any ideas?


Thanks again for your help

Paul
 
I

Ixa

<xsl:if test="not(preceding-sibling::partialPerson
[key('person', *) = key('person', $identifier)])">
This bit was interesting - I'm using Oxygen XML to develop the
transforms, and with Saxon 6.5.5 it works fine, but with the included
Xalan (Java version 2.7.0) the line above gives an 'Unknown Error in
XPath' and dies.

Sounds like Xalan-J has some issues with comparing node-set given by
"key"- function.

Anyhow, actually this node-set comparison can also (and should) be done
without "key"- function so that it works with Xalan-J, too. Using
"key"- functions in this test is just unnecessary. For example:

---8<---8<---
<xsl:for-each select="PartialPerson">
<xsl:variable name="children" select="*"/>
<xsl:if test="not(preceding-sibling::partialPerson[* = $children])">
<xsl:apply-templates select="."/>
</xsl:if>
</xsl:for-each>
---8<---8<---
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,997
Messages
2,570,241
Members
46,832
Latest member
UtaHetrick

Latest Threads

Top