Search code examples
xqueryexcept

return whole element using except() using xquery


Currently I'm doing some xquery like following.

XML document:

<?xml version="1.0" encoding="UTF-8"?>
<animal>
    <dog name="ada">
        <color>black</color>
        <byear>2012</byear>
    </dog>
    <dog name="bob">
        <color>black</color>
        <byear>2011</byear>
    </dog>
    <cat name="cathy">
        <color>black</color>
        <byear>2010</byear>
    </cat>
    <cat name="dione">
        <color>brown</color>
        <byear>2009</byear>
    </cat>
</animal>

Expected output:

<?xml version="1.0" encoding="UTF-8"?>
<animal>
    <color>black</color>
    <dog name="ada">
        <byear>2012</byear>
    </dog>
    <dog name="bob">
        <byear>2011</byear>
    </dog>
    <cat name="cathy">
        <byear>2010</byear>
    </cat>
</animal>

My code looks like:

<animal>
{ for $a in distinct-values(//animal/*/color)
    return <color>{$a}</color>
    { for $b in //animal/*[color=$a]
    where $b//color="black"
        return $b/* except $b/color
</animal>

and the output was good but it does not include the parent tag (e.g. <dog name="ada"> </dog>).

Besides, I had also tried something like return $b except $b/color and the output this time included parent tag however it also included the "color" on the child tag. Any idea?


Solution

  • In normal XQuery you cannot just return a part of a node. You either return the entire node, with all its children/descendants, or you select some of the children/descendants and return those nodes entirely.

    What you can do instead is to create a new node with the same name as the old one with element {node-name(...)} {...} and then copy the children you want from the old node to the new one.

    Something like:

    <animal>
    { for $a in distinct-values(//animal/*/color)
        return (
          <color>{$a}</color>,
          for $b in //animal/*[color=$a]
          return element {node-name($b)} {$b / @*,  $b/node() except $b/color }
        )
    }
    </animal>
    

    (With the XQuery Update extension you could also remove the nodes you do not want instead copying them)