Element constructors: computed and direct
Author: Dave Cassel | Category: Software DevelopmentIn XQuery, you can build an element one of two ways: computed or direct.
Direct
This is the simpler case, so I’ll take it first. Direct means that your XQuery code has the XML you want to build:
declare namespace blog = "http://davidcassel.net/blog";
<blog:example simple="true"> Â <blog:pointless/> </blog:example>
XML is a natural data structure for XQuery and this is a very simple way to construct it.
Computed
A computed element is less direct, but it has its benefits. Let’s take a look:
declare namespace blog = "http://davidcassel.net/blog"; element { xs:QName("blog:example") } { attribute simple { "true" }, element { xs:QName("blog:pointless") } { } }
Benefits
The direct method is more concise, so why would we use the computed? There are two reasons why I typically end up using them.
A Conditional Attribute
Sometimes you want to include an attribute only under certain circumstances. The trick is, this doesn’t work:
<blog:example { if ($condition) then simple="true" else () }/>
That’s not valid syntax — the attribute either has to be there or not, though its value can be the result of an XQuery expression. One way we can conditionally include an attribute is to build the element different in the if and else branches of the condition:
if ($condition) then  <blog:example simple="true"/> else  <blog:example/>
As you can imagine, that can be a lousy way to go if the element has some complexity to it, for instance if it has a lot of attributes. This is one place where computed construction makes your life easier:
declare namespace blog = "http://davidcassel.net/blog"; element { xs:QName("blog:example") } { if ($condition) then attribute simple { "true" } else (), element { xs:QName("blog:pointless") } { } }
Computing the Name
Another case where the computed version comes in handy is when the name of the element itself will be the result of a computation. Let’s suppose you have a map that you want to turn into a set of elements.
<root>{ Â for $key in map:keys($map) Â return element { $key } { map:get($map, $key) } }</root>
There’s really no other way to do this case, but it’s simple to do this way.
Any other cases you can think of where computed elements are the easier way to go?
Tags: techniques, xquery
November 21st, 2011 at 2:12 pm
Nice post, Dave. Note you also have the option of using a computed attribute constructor with a direct element constructor. So your conditional attribute example could be simplified like this:
<blog:example>
{ if ($condition) then attribute simple { “true” } else () }
<blog:pointless/>
</blog:example>
In general, I think it’s a good practice to avoid computed constructors except in the cases you describe. Direct constructors are much more readable, like you said.
November 21st, 2011 at 2:17 pm
Good point, Evan. I’ve done that mixed approach accidentally before (trying to include a value, but getting the whole attribute instead), but I don’t tend to think of doing it that way on purpose.
November 5th, 2014 at 8:15 am
Thanks for this
The article (together with the comment) were exactly what I was looking for
November 18th, 2014 at 4:57 am
Thanks Dave, I’m echoing Peter’s comment – a nice easily understood explanation and example
December 3rd, 2015 at 9:38 am
Exactly what I was struggling with: conditionally adding an attribute. Thank you.
April 20th, 2018 at 1:52 am
Thanks David. Great article and real life saver.
Regards