0

I have the following xml:

<subjects>
   <subject id="5" name="Maths"/>
   <subject id="10" name="English" points="5"/>
   <subject id="15" name="Science" points="10" cats="15"/>
</subjects>

and I have the following code:

for $subject in $subjects/subject
return 
     <subject id="{$subject/@id}">
          <foo/>
     </subject>

which outputs

<subject id="5"><foo/></subject>
<subject id="10"><foo/></subject>
<subject id="15"><foo/></subject>

However the above code does not show all the attributes of subject. Only id. Of course I can continue hardcoding more attributes but this won't help if I don't know what the attributes names are.

What I want is something like this:

<subject id="5" name="Maths"><foo/></subject>
<subject id="10" name="English" points="5"><foo/></subject>
<subject id="15" name="Science" points="10" cats="15"><foo/></subject>

I believe the solution would be something like this:

for $subject in $subjects/subject
return 
     insert-child( <foo/>, $subject)

How do I do this?

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
Yahya Uddin
  • 26,997
  • 35
  • 140
  • 231
  • 1
    XPath has no support for insertions at all; what you're doing is XQuery, not XPath. – Charles Duffy Dec 01 '15 at 16:16
  • 1
    I also strongly suggest reading the documentation for XQUF (the XQuery Update Facility). – Charles Duffy Dec 01 '15 at 16:17
  • @Charles Duffy Is this feature only available to Saxon EE http://www.saxonica.com/html/documentation/using-xquery/update.html cause I am using the free community edition (CE) – Yahya Uddin Dec 01 '15 at 16:18
  • 1
    I'm not closely familiar with Saxon -- my preferred XQuery implementation is BaseX -- so I couldn't say off the top of my head. – Charles Duffy Dec 01 '15 at 16:19

2 Answers2

2

Once your XML source becomes more complicated, if your task is to make small changes to a large XML document then I would strongly recommend using XSLT rather than XQuery. XSLT's recursive-descent processing model based on matching template rules is absolutely cut out for this task, and if you try to do it in XQuery you end up replicating this mechanism "by hand" in your own recursive functions.

An alternative is XQuery Update (which, to answer your question, is supported in commercial editions of Saxon). I'm no great enthusiast of XQuery Update, except perhaps in a database context, but it does the job and some people like it.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • As far as I'm aware XQuery update, updates the orgional document it self. What if I just want to edit my output like I shown in my question. – Yahya Uddin Dec 02 '15 at 12:43
  • 1
    @YahyaUddin As Dr. Kay suggests, for simple tasks XQuery may be sufficient (see examples of a recursive `typeswitch` pattern), but for complex rules, XSLT is better suited for the task. – wst Dec 02 '15 at 15:59
  • 1
    @YahyaUddin XQuery Update includes a copy...modify (aka "transform") expression which creates a modified copy of the original, rather than updating the original in situ. – Michael Kay Dec 02 '15 at 16:26
1

You were on the right track. As you are rewriting the elements, you can simply use a wildcard attribute selector to copy all of the attributes from the context node to your newly constructed element.

for $subject in $subjects/subject
return 
  <subject>{
      $subject/@*,
      <foo/>
  }</subject>
wst
  • 11,681
  • 1
  • 24
  • 39