2
votes

I want to extract the name of all the guides that know English from an XML-document, the problem is that I don't know how. I use XQuisitor to query the XML-document.

Snippet:

<GuideLang>
<guideID>1</guideID>
<lname>English</lname>
</GuideLang>
<GuideLang>
<guideID>2</guideID>
<lname>German</lname>
</GuideLang>
<GuideLang>
<guideID>3</guideID>
<lname>Swedish</lname>
</GuideLang>
<Guide>
<guideID>1</guideID>
<gname>John Smith</gname>
</Guide>
<Guide>
<guideID>2</guideID>
<gname>Weber Schneider</gname>
</Guide>

I know that:

 element Result {
 //GuideLang[lname='English']
 }

Gives me all the info about the language and the guide's ID - but how can I use that ID to only print the guide's name from the Guide node?

Thanks!

3
This is only an "assignment" from a book I'm reading, and I figure that if I know the answer to this particular question then I'll be able to figure out the rest of them. - Michael Kaesy

3 Answers

4
votes

Use this pure XPath 2.0 expression:

/*/Guide
   [for $gid in guideID
     return ../GuideLang[guideID eq $gid and lname eq 'English']
   ]
    /gname

This supposes that the nodes of the provided document fragment are children of the (not provided) top element of the (not provided) XML document.

This can be converted even to an XPath 1.0 expression (note that no // is used!):

/*/Guide
    [guideID
    =
     ../GuideLang[lname = 'English']/guideID
    ]
     /gname

Update:

Just for the people that are surprized that the first expression above is pure XPath 2.0, here is a complete XSLT 2.0 - based verification:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/*">
     <xsl:sequence select=
     "/*/Guide
           [for $gid in guideID
             return ../GuideLang[guideID eq $gid and lname eq 'English']
           ]
            /gname
     "/>
 </xsl:template>
</xsl:stylesheet>

when this XSLT 2.0 transformation is applied on the following document (the provided fragment, wrapped into a top element):

<t>
    <GuideLang>
        <guideID>1</guideID>
        <lname>English</lname>
    </GuideLang>
    <GuideLang>
        <guideID>2</guideID>
        <lname>German</lname>
    </GuideLang>
    <GuideLang>
        <guideID>3</guideID>
        <lname>Swedish</lname>
    </GuideLang>
    <Guide>
        <guideID>1</guideID>
        <gname>John Smith</gname>
    </Guide>
    <Guide>
        <guideID>2</guideID>
        <gname>Weber Schneider</gname>
    </Guide>
</t>

the XPath 2.0 expression is evaluated and the correctly selected node is output:

<gname>John Smith</gname>
2
votes

You don't even need to use XQuery, XPath is fine, too:

//Guide[guideID=(//GuideLang[lname="English"]/guideID)]/gname

This query fetches all guides whose guideID equals one of the guideIDs which are known to speak English and extracts their gname.

Make sure to know about XQuery's =-operator's set semantics!

2
votes

The most straight-forward way to do that, would be something like this:

let $english-guide-ids := //GuideLang[lname = 'English']/guideID
return
    //Guide[guideID = $english-guide-ids]

The 'let' statement is an XQuery-specific extension of the XPath 2.0 FLWOR statement, but very convenient to keep the code readable.

Some may argue you should try to limit the use of // as much as possible. But XQuery often runs within XML databases, and they are often able to optimize such expressions. For a small assignment, I wouldn't worry to much about performance though..