Get node value from two dependent xml files

154 views Asked by At

I am a beginner in Xquery and I have an xml code in which I want to dependent two different XML file with another file.

Book.xml

<library>
   <Book>
     <title>Title1</title>
     <author>Ellizabith</author>
   </Book>
   <Book>
     <title>Title2</title>
     <author>Sam</author>
   </Book>
   <Book>
    <title>Title3</title>
    <author>Ryan</author>
   </Book>
</library>

author.xml

 <authorRoot>
   <author>
    <Name>Rayan</Name>
     <Location>Yahoo</Location>
    </author>
    <author>
      <Name>Syan</Name>
      <Location>Google</Location>
    </author>
    <author>
      <Name>Sam</Name>
      <Location>Bing</Location>
     </author>
    </authorRoot>

in this answer a query to show the location of all the authors of book whose title contains the word "Title2".

This is my code :

 for $p in doc("C:\Users\User\Desktop\Book.xml")//library/book/[title/contains(.,   'Title1')]
 for $a in doc("C:\Users\User\Desktop\author.xml")//authorRoot
 let $p := $p/author/text()
 let $d := $a/author
 let $f := $d/text()=$p/Location/text()
 return $f
2

There are 2 answers

1
Jens Erat On BEST ANSWER

There are multiple smaller problems with your codes.

  1. Names in authors and books XML files do not match. I guess it's only some typos.
  2. Predicates belong to an axis step, not in their own (remove the slash after book in line one).
  3. XML and XQuery are capitalization sensitive! <Book/> uses a capital B, so do the same in your XQuery, again in line 1.
  4. In Line two, you're looping over all <authorRoot/> elements. Use authorRoot/author instead.
  5. In line three you're hiding the book $p with it's name for the rest of this FLWOR expression, but you want to use the book again in line five. Better use another variable name.
  6. Better do not use the descendant-or-self-step // if you don't need it (lines one and two). This decreases performance.

I don't get what your idea for filtering was in lines three to five. Compare yourself with this working solution. Additionally I used speaking variable names, don't confuse yourself with unnecessary short ones.

Replace $book and $authors by the respecting doc(...) functions.

for $book in $books//library/Book[title/contains(.,   'Title1')]
for $author in $authors//authorRoot/author
where $book/author = $author/Name
return $author/Location/text()

If you want to have a list of distinct places, wrap distinct-values(...) around all four lines.

An alternative without explicit loops:

$authors/authorRoot/author[
  Name = $books/library/Book[contains(title, 'Title1')]/author)
]/Location

The second solution is also valid XPath 1.0, the first requires an XPath 3.0 or XQuery processor.

0
Martin Honnen On

If you use

let $books := <library>
   <Book>
     <title>Title1</title>
     <author>Ellizabith</author>
   </Book>
   <Book>
     <title>Title2</title>
     <author>Sam</author>
   </Book>
   <Book>
    <title>Title3</title>
    <author>Ryan</author>
   </Book>
</library>
let $authors := <authorRoot>
   <author>
    <Name>Rayan</Name>
     <Location>Yahoo</Location>
    </author>
    <author>
      <Name>Syan</Name>
      <Location>Google</Location>
    </author>
    <author>
      <Name>Sam</Name>
      <Location>Bing</Location>
     </author>
     <author>
         <Name>Ellizabith</Name>
         <Location>Apple</Location>
    </author>
    </authorRoot>
    return $authors/author[Name = $books/Book[contains(title, 'Title1')]/author]/Location/string()

the result is Apple.