Most queries include some XML elements and attributes that structure the results. In the previous chapter, we saw how to use path expressions to copy elements and attributes from input documents. After a brief review of this technique, this chapter explains how you can create entirely new elements and attributes and include them in your results.
There are two ways to create new elements and attributes: direct constructors and computed constructors. Direct constructors, which use an XML-like syntax, are useful for creating elements and attributes whose names are fixed. Computed constructors, on the other hand, allow for names that are generated dynamically in the query.
Some queries simply include elements and attributes from the input document in the results. Example 5-1 includes certain selected name
elements in the results.
Query
for
$
prod
in
doc
(
"catalog.xml"
)/
catalog
/
product
[
@dept
=
'ACC'
]
return
$
prod
/
name
Results
<name
language=
"en"
>
Floppy Sun Hat</name>
<name
language=
"en"
>
Deluxe Travel Bag</name>
Note that because the entire name
element is returned, the results include the name
elements, not just their atomic values. In fact, if the query returns elements that have attributes and descendants, they are all part of the results. This is exhibited in Example 5-2.
Query
for
$
prod
in
doc
(
"catalog.xml"
)/
catalog
/
product
[
@dept
=
'ACC'
]
return
$
prod
Results
<product
dept=
"ACC"
>
<number>
563</number>
<name
language=
"en"
>
Floppy Sun Hat</name>
</product>
<product
dept=
"ACC"
>
<number>
443</number>
<name
language=
"en"
>
Deluxe Travel Bag</name>
</product>
The product
elements are included as they appear in the input document, with all attributes and children. If they are in a namespace in the input document, they will be in that same namespace in the results. There is no opportunity to add or remove children or attributes, or change the namespace name, when using path expressions. Techniques for making such modifications are covered later in this chapter.
You can also insert your own XML elements and attributes into the query results by using XML constructors. There are two kinds of XML constructors: direct constructors, which use familiar XML-like syntax, and computed constructors, that allow you to generate dynamically the XML names used in the results.
A direct element constructor is a constructor of the first kind; it specifies an XML element (optionally with attributes) by using XML-like syntax, as shown in Example 5-3. The result of the query is an XHTML fragment that presents the selected data.
The h1
, ul
, and li
elements appear in the results as XML elements. The h1
element constructor simply contains literal characters Product Catalog
, which appear in the results as the content of h1
. The ul
element constructor, on the other hand, contains another XQuery expression enclosed in curly braces. This is known as an enclosed expression, and its value becomes the content of the ul
element in the results. In this case, the enclosed expression evaluates to a sequence of li
elements, which then appear as children of the ul
element in the results. An enclosed expression may also evaluate to one or more atomic values, which appear in the results as character data.
Query
<html>
<h1>
Product Catalog
</h1>
<ul>
{
for
$
prod
in
doc
(
"catalog.xml"
)/
catalog
/
product
return
<li>
number:
{
data
(
$
prod
/
number
)}
, name:
{
data
(
$
prod
/
name
)}
</li>
}
</ul>
</html>
Results
<html>
<h1>
Product Catalog</h1>
<ul>
<li>
number: 557, name: Fleece Pullover</li>
<li>
number: 563, name: Floppy Sun Hat</li>
<li>
number: 443, name: Deluxe Travel Bag</li>
<li>
number: 784, name: Cotton Dress Shirt</li>
</ul>
</html>
The li
element constructor contains a combination of both literal characters (the strings number:
and , name:
) and enclosed expressions, each of which evaluates to an atomic value. Any element constructor content outside curly braces is considered a literal, no matter how much it looks like an expression.
Direct element constructors use a syntax that looks very much like XML. The tags use the same angle-bracket syntax, the names must be valid XML names, and every start tag must have a matching end tag that is properly nested. In addition, prefixed names can be used, and even namespace declarations included. As with regular XML, the attributes of a direct element constructor must have unique names. But there are a few differences from real XML. For example, expressions within curly braces can use the < operator without escaping it.
As shown in Example 5-3, element constructors can contain literal characters, other element constructors, and enclosed expressions, in any combination.
Literal characters are characters that appear outside of enclosed expressions in element constructor content. Literal characters from Example 5-3 include the string Product Catalog
in the h1
element constructor, and the string , name:
in the li
element constructor.
In addition, the literal characters can include character and predefined entity references such as  
and <
and CDATA sections (described in “CDATA Sections”). As in XML content, the literal characters cannot include unescaped less-than (<) or ampersand (&) characters; they must be escaped using <
and &
, respectively.
When a curly brace is to be included literally in the content of an element, it must be escaped by doubling it, that is, {{
for the left curly brace, or }}
for the right.
Direct element constructors can also contain other direct element constructors. In Example 5-4, the html
element constructor contains constructors for h1
and ul
. They are included directly within the content of html
, without curly braces. No special separator is used between them. The p
element constructor contains a combination of character data content, a direct element constructor (for the element i
), and an enclosed expression. As you can see, these three things can be intermingled as necessary.
Query
<html>
<h1>
Product Catalog
</h1>
<p>
A
<i>
huge
</i>
list of
{
count
(
doc
(
"catalog.xml"
)//
product
)}
products.
</p>
</html>
Results
<html>
<h1>
Product Catalog</h1>
<p>
A<i>
huge</i>
list of 4 products.</p>
</html>
In Example 5-3, the enclosed expression of the ul
element evaluates to a sequence of elements. In fact, it is possible for the enclosed expression to evaluate to a sequence of attributes or other nodes, atomic values, or even a combination of nodes and atomic values. It can even evaluate to a document node, in which case, that document node is replaced by its children.
As you have seen with the li
elements, elements in the sequence become children of the element being constructed (in this case, ul
). Atomic values, on the other hand, become character data content. If the enclosed expression evaluates to a sequence of both elements and atomic values, as shown in Example 5-5, the result element has mixed content, with the order of the child elements and character data preserved.
Query
for
$
prod
in
doc
(
"catalog.xml"
)/
catalog
/
product
return
<li>
number:
{
$
prod
/
number
}
</li>
Results
<li>
number:<number>
557</number></li>
<li>
number:<number>
563</number></li>
<li>
number:<number>
443</number></li>
<li>
number:<number>
784</number></li>
Example 5-3 used the data
function in enclosed expressions to extract the values of the elements number
and name
. In this example, the number
element is included without applying the data
function. The results are somewhat different; instead of just the number value itself, the entire number
element is included.
If an element constructor contains an enclosed expression that evaluates to one or more attributes, these attributes become attributes of the element under construction. This is exhibited in Example 5-6, where the enclosed expression {$prod/@dept}
has been added at the beginning of the li
constructor content.
Query
for
$
prod
in
doc
(
"catalog.xml"
)/
catalog
/
product
return
<li>
{
$
prod
/
@dept
}
number:
{
$
prod
/
number
}
</li>
Results
<li
dept=
"WMN"
>
number:<number>
557</number></li>
<li
dept=
"ACC"
>
number:<number>
563</number></li>
<li
dept=
"ACC"
>
number:<number>
443</number></li>
<li
dept=
"MEN"
>
number:<number>
784</number></li>
The dept
attribute appears in the results as an attribute of the li
element rather than as content of the element. If the example had used the data
function within the enclosed expression, the value of the dept
attribute would have been the first character data content of the li
element.
Enclosed expressions that evaluate to attributes must appear first in the element constructor content, before any other kinds of nodes.
If an enclosed expression evaluates to one or more atomic values, those values are simply cast to xs:string
and included as character data content of the element. When adjacent atomic values appear in the expression sequence, they are separated by a space in the element content. For example:
<li>{"x", "y", "z"}</li>
will return <li>x y z</li>
, with spaces. To avoid this, you can use three separate expressions, as in:
<li>{"x"}{"y"}{"z"}</li>
Another option is to use the concat
function to concatenate them together into a single expression, as in:
<li>{concat("x", "y", "z")}</li>
Enclosed expressions may include more than one sub-expression inside the curly braces, using commas as separators. In Example 5-7, the enclosed expression in the li
constructor contains four different sub-expressions, separated by commas.
Query
for
$
prod
in
doc
(
"catalog.xml"
)/
catalog
/
product
return
<li>
{
$
prod
/
@dept
,
"string"
,
5
+
3
,
$
prod
/
number
}
</li>
Results
<li
dept=
"WMN"
>
string 8<number>
557</number></li>
<li
dept=
"ACC"
>
string 8<number>
563</number></li>
<li
dept=
"ACC"
>
string 8<number>
443</number></li>
<li
dept=
"MEN"
>
string 8<number>
784</number></li>
The first sub-expression, $prod/@dept
, evaluates to an attribute, and therefore becomes an attribute of li
.
The next two sub-expressions, "string"
and 5+3
, evaluate to atomic values: a string and an integer, respectively. Note that they are separated by a space in the results.
The final sub-expression, $prod/number
, is an element, which is not separated from the atomic values by a space.
You have seen how attributes can be included with the result elements by including enclosed expressions that evaluate to attributes. Attributes can also be constructed directly using XML-like syntax. Attribute values can be specified using literal text or enclosed expressions, or a combination of the two.
In Example 5-8, class
and dep
attributes are added to the h1
and li
elements, respectively. The class
attribute of h1
simply includes literal text that is repeated in the results. The dep
attribute of li
, on the other hand, includes an enclosed expression that evaluates to the value of the dept
attribute of that product
. Do not let the quotes around the expression fool you; anything in curly braces is evaluated as an enclosed expression.
Query
<html>
<h1
class
=
"
itemHdr
"
>
Product Catalog
</h1>
<ul>
{
for
$
prod
in
doc
(
"catalog.xml"
)/
catalog
/
product
return
<li
dep
=
"{
$
prod
/
@dept
}"
>
number:
{
data
(
$
prod
/
number
)
}
, name:
{
data
(
$
prod
/
name
)}
</li>
}
</ul>
</html>
Results
<html>
<h1
class=
"itemHdr"
>
Product Catalog</h1>
<ul>
<li
dep=
"WMN"
>
number: 557, name: Fleece Pullover</li>
<li
dep=
"ACC"
>
number: 563, name: Floppy Sun Hat</li>
<li
dep=
"ACC"
>
number: 443, name: Deluxe Travel Bag</li>
<li
dep=
"MEN"
>
number: 784, name: Cotton Dress Shirt</li>
</ul>
</html>
Note that the dep
attribute will appear regardless of whether there is a dept
attribute of the $prod
element. If the $prod
element has no dept
attribute, the dep
attribute’s value will be a zero-length string. This is in contrast to Example 5-7, where li
will have a dept
attribute only if $prod
has a dept
attribute.
If literal text is used in a direct attribute constructor, it follows similar rules to the literal text in element constructors. Also, as with XML syntax, quote characters in attribute values must be escaped if they match the kind of quotes (single or double) used to delimit that value. However, you don’t need to escape quotes appearing in an expression inside curly braces. The following example is valid because the inner pair of double quotes is inside curly braces:
<li dep="{substring-after($prod/@dept, "-")}"/>
The evaluation of enclosed expressions in attribute values is slightly different from those in element content. Because attributes cannot themselves have children or attributes, the attribute value must evaluate to an atomic value. Therefore, if an enclosed expression in an attribute value evaluates to one or more elements or attributes, the value of the node(s) is extracted and converted to a string.
In Example 5-8, the enclosed expression {$prod/@dept}
for the dep
attribute of li
evaluates to an attribute. The processor did not attempt to add a dept
attribute to the dep
attribute (which would not make sense). Instead, it extracted the value of the dept
attribute and used this as the value of the dep
attribute.
Just as in XML, you can specify multiple attributes on an element, as long as they have unique names. The order of the attributes is never considered significant in XML, so your attributes might not appear in your result document in the same order as you specified them in the query. There is no way to force the processor to preserve attribute order.
In addition to regular attributes, you can also include namespace declarations in direct element constructors. These namespace declaration attributes affect the element itself and all its descendants, and override any namespace declarations in the prolog or in outer element constructors. Example 5-9 shows the use of a namespace declaration in an element constructor. This is discussed in detail in “Namespace Declarations in Direct Element Constructors”.
Query
<xhtml:html
xmlns:xhtml
=
"
http://www.w3.org/1999/xhtml
"
>
<xhtml:h1
class
=
"
itemHdr
"
>
Product Catalog
</xhtml:h1>
<xhtml:ul>
{
for
$
prod
in
doc
(
"catalog.xml"
)/
catalog
/
product
return
<xhtml:li
class
=
"{
$
prod
/
@dept
}"
>
number:
{
data
(
$
prod
/
number
)}
</xhtml:li>
}
</xhtml:ul>
</xhtml:html>
Results
<xhtml:html
xmlns:xhtml=
"http://www.w3.org/1999/xhtml"
>
<xhtml:h1
class=
"itemHdr"
>
Product Catalog</xhtml:h1>
<xhtml:ul>
<xhtml:li
class=
"WMN"
>
number: 557</xhtml:li>
<xhtml:li
class=
"ACC"
>
number: 563</xhtml:li>
<xhtml:li
class=
"ACC"
>
number: 443</xhtml:li>
<xhtml:li
class=
"MEN"
>
number: 784</xhtml:li>
</xhtml:ul>
</xhtml:html>
Suppose you want to include elements from the input document but want to make minor modifications such as adding or removing a child or attribute. To do this, a new element must be created using a constructor. For example, suppose you want to include product
elements from the input document, but add an additional attribute id
that is equal to the letter P
plus the product number. The query shown in Example 5-10 accomplishes this.
Query
for
$
prod
in
doc
(
"catalog.xml"
)/
catalog
/
product
[
@dept
=
'ACC'
]
return
<product
id
=
"
P
{
$
prod
/
number
}"
>
{
$
prod
/(
@*
,
*
)}
</product>
Results
<product
dept=
"ACC"
id=
"P563"
>
<number>
563</number>
<name
language=
"en"
>
Floppy Sun Hat</name>
</product>
<product
dept=
"ACC"
id=
"P443"
>
<number>
443</number>
<name
language=
"en"
>
Deluxe Travel Bag</name>
</product>
The query makes a new copy of the product
element, which contains the enclosed expression {$prod/(@*, *)}
to copy all the attributes and child elements from the original product
element. You could also use the broader expression {$prod/(@*, node())}
to copy all the child nodes of the element, including text, comments, and processing instructions.
As another example, suppose you want to copy some product
elements from the input document but remove the number
child. This can be accomplished using the query in Example 5-11. The enclosed expression $prod/(@*, * except number)
selects all the attributes and all the child elements of product
except number
.
Query
for
$
prod
in
doc
(
"catalog.xml"
)/
catalog
/
product
[
@dept
=
'ACC'
]
return
<product>
{
$
prod
/(
@*
,
*
except
number
)}
</product>
Results
<product
dept=
"ACC"
>
<name
language=
"en"
>
Floppy Sun Hat</name>
</product>
<product
dept=
"ACC"
>
<name
language=
"en"
>
Deluxe Travel Bag</name>
</product>
Additional examples of making “modifications” to elements and attributes can be found in “Copying Input Elements with Modifications”.
Whitespace is often used in direct element constructors. For example, you may use line breaks and tabs to indent result XML elements for readability, or spaces to separate enclosed expressions. Sometimes the query author intends for whitespace to be significant (included in the results); sometimes it is just used for formatting the query for visual presentation.
Boundary whitespace is whitespace that occurs by itself (without any non-whitespace characters) in direct element constructors. It may appear between two element constructor tags, between two enclosed expressions, or between a tag and an enclosed expression. It can be made up of any of the XML whitespace characters, namely space, tab, carriage return, and line feed.
For example, in the constructor shown in Example 5-12, there is boundary whitespace in the ul
constructor between the ul
start tag and the left curly brace, as well as between the right curly brace and the ul
end tag. In the li
constructor, there is boundary whitespace between the li
start tag and the b
start tag, between the b
end tag and the left curly brace, and between the right curly brace and the li
end tag.
<ul>
{
<li>
<b>
number:
</b>
{
"557"
}
</li>
}
</ul>
With boundary whitespace discarded, the results look something like:
<ul><li><b> number:</b><number>557</number></li></ul>
Note that the space before the text number:
is not discarded because it appears with other characters. Of course, if you choose to serialize and indent your results, new whitespace will be added for the indentation. Therefore, your results may vary.
Whitespace inside enclosed expressions that is not in quotes is never considered significant. It is simply the normal whitespace allowed by XQuery syntax. In the ul
constructor, the spaces between the left curly brace and the li
start tag fall into this category. It is not technically considered boundary whitespace, and it is always discarded. Using an xml:space
attribute on a direct element constructor has no effect on the treatment of boundary whitespace.
There is no boundary whitespace in attribute values. For example, in the expression:
<product dept=" {$d} "/>
the whitespace between the quotes and the enclosed expression is considered significant and therefore is preserved. The expression:
<product dept="{ $d }"/>
has no boundary whitespace either, only whitespace in an enclosed expression. This whitespace is not preserved. Line breaks are never preserved in attribute values; they are converted to spaces. This is a standard feature of XML itself, known as attribute value normalization.
By default, a query processor discards all boundary whitespace. Sometimes you want to preserve the boundary whitespace in your query results because it is significant. The boundary-space declaration, specified in the query prolog, instructs the processor how to handle boundary whitespace in direct element constructors. Its syntax is shown in Figure 5-1.
The two valid values are:
preserve
strip
The default is strip
. For example, the boundary-space declaration:
declare boundary-space preserve;
causes whitespace to be preserved. With this boundary-space declaration, the result of the constructor in Example 5-12 becomes:
<ul> <li> <b> number:</b> <number>557</number> </li> </ul>
Table 5-1 shows some additional examples of results with and without preserved whitespace.
Expression | Value with boundary whitespace preserved | Value with boundary whitespace stripped |
---|---|---|
<e> <c></c> </e> | <e> <c></c> </e> | <e><c></c></e> |
<e> {"x"} </e> | <e> x </e> | <e>x</e> |
<e> {()} </e> | <e> </e> | <e></e> |
<e>{"x"} {"y"}</e> | <e>x y</e> | <e>xy</e> |
<e> x {"y"}</e> | <e> x y</e> | <e> x y</e> |
<e>{" x "}</e> | <e> x </e> | <e> x </e> |
<e>{ "x" }</e> | <e>x</e> | <e>x</e> |
<e>   {"x"}</e> | <e> x</e> | <e> x</e> |
<e> </e> | <e> </e> | <e></e> |
If you don’t want to preserve all whitespace but wish to preserve it in one or more specific elements, you can do this in one of two ways. The first way is to include an enclosed expression that evaluates to whitespace. For example, <e>{" x "}</e>
evaluates to <e> x </e>
, regardless of the boundary-space declaration. This is because the whitespace is part of the value of the expression (the literal string).
Another method is to use a character reference to a whitespace character. Whitespace that is the result of a character reference is always considered significant. For example, <e>   {"x"}</e>
always evaluates to <e> x</e>
. Character references are described further in “XML Entity and Character References”.
Generally, if you know the element or attribute name that you want to use in your results, you can use XML-like syntax as described in the previous sections. However, sometimes you may want to compute the name dynamically, so you cannot include the literal names in the query. In this case, you use computed constructors. This can be useful when:
You want to simply copy elements from the input document (regardless of name) but make minor changes to their content. For example, you want to add an id
attribute to every element, or move all the elements to a different namespace.
You want to turn content from the input document into element or attribute names. For example, you want to create an element whose name is the value of the dept
attribute in the input document, without a predefined list of elements.
You want to look up element names in a separate dictionary, e.g., for language translation purposes.
You can use computed constructors for elements, attributes, and other kinds of nodes.
A computed element constructor uses the keyword element
, followed by a name and some content in curly braces. The syntax of a computed element constructor is shown in Figure 5-2.
Example 5-13 shows a query that is equivalent to Example 5-3, except that it uses computed element constructors instead of direct ones.
Query
element
html
{
element
h1
{
"Product Catalog"
},
element
ul
{
for
$
prod
in
doc
(
"catalog.xml"
)/
catalog
/
product
return
element
li
{
"number:"
,
data
(
$
prod
/
number
),
", name:"
,
data
(
$
prod
/
name
)}
}
}
Results
<html>
<h1>
Product Catalog</h1>
<ul>
<li>
number: 557 , name: Fleece Pullover</li>
<li>
number: 563 , name: Floppy Sun Hat</li>
<li>
number: 443 , name: Deluxe Travel Bag</li>
<li>
number: 784 , name: Cotton Dress Shirt</li>
</ul>
</html>
The name in a computed element constructor is represented by either a qualified name or an expression (in curly braces) that evaluates to a qualified name. This is then followed by an enclosed expression (also in curly braces) that contains the content of the element. For example, the constructor:
element h1 { "Product Catalog" }
uses a literal name to construct the element <h1>Product Catalog</h1>
, while:
element {concat("h", $level)} { "Product Catalog" }
uses an expression, enclosed in curly braces, to dynamically generate the name by concatenating the literal h
with a variable value.
You could also copy the name of the new node from an existing node, using the node-name
function. For example:
element {node-name($myNode)} { "contents" }
will give the new element the same name as the node that is bound to the $myNode
variable.
The expression used for the name can be untyped, or it can be either an xs:QName
(which is what node-name
returns) or an xs:string
value. It can even be a node, in which case it is atomized to extract its typed value (not its name).
Default namespace declarations apply to element constructors. If you do not prefix your names, and you declare a default element namespace (e.g., in an outer expression or in the query prolog), the new elements are considered to be in that namespace.
After the name, the next part of the computed element constructor is an enclosed expression that contains the contents of the element, including attributes, character data, and child elements. As with direct XML constructors, any elements returned by the enclosed expression become children of the new element, attributes become attributes, and atomic values become character data content. Computed constructors have the same rule that the attributes must appear before any elements or character data.
The syntax is slightly different for computed constructors in that there can only be one pair of curly braces, containing the attributes and contents of the element. If several expressions are needed for the attributes, child elements, and character data content, they are separated by commas. For example, when using a direct element constructor, you can construct the li
element this way:
<li>number: {data($prod/number)} , name: {data($prod/name)}</li>
where you intersperse literal text and enclosed expressions. To create an identical li
element using a computed constructor, you would use the syntax:
element li {"number:", data($prod/number), ", name:", data($prod/name)}
Note that the literal text is in quotes and is separated from the expressions by commas. Also, the expressions, such as data($prod/number)
, are not themselves enclosed in curly braces as they were with the direct constructor.
The values of each of the four expressions in the li
constructor will be separated by spaces in the results. If you do not wish to have those spaces in the results, you can use the concat
function to concatenate the values together, as in:
element li {concat("number:", data($prod/number), ", name:", data($prod/name))}
If you want the constructed element to be empty, you can put nothing between the curly braces (as in { }
), but the braces are still required.
A computed attribute constructor has syntax identical to a computed element constructor, except that it uses the keyword attribute
. Its syntax is shown in Figure 5-3.
For example, the constructors:
attribute myattr { $prod/@dept }
and:
attribute {concat("my", "attr")} { $prod/@dept }
both construct an attribute whose name is myattr
and whose value is the same as the dept
attribute of $prod
. As with direct attribute constructors, any elements or attributes that are returned by the expression have their values extracted and converted to strings.
Computed attribute constructors are not just for use in computed element constructors. They can be used in direct element constructors as well, if they are included in an enclosed expression. For example, the expression:
<result>{attribute {concat("my", "attr")} { "xyz" } }</result>
will return the result:
<result myattr="xyz"/>
You cannot construct namespace declarations by using computed attribute constructors. If the name specified for an attribute constructor is xmlns
, or a name whose prefix is xmlns
, error XQDY0044
is raised. For example, the following constructor is invalid:
attribute xmlns:prod { "http://datypic.com/prod" }
Instead, you should declare the namespace in the query prolog or in an outer direct element constructor.
One application of computed constructors is to transform content into markup. For example, suppose you want to create a product catalog that has the names of the departments as element names instead of attribute values. The query in Example 5-14 can be used for this purpose.
Query
for
$
dept
in
distinct-values
(
doc
(
"catalog.xml"
)/
catalog
/
product
/
@dept
)
return
element
{
$
dept
}
{
doc
(
"catalog.xml"
)/
catalog
/
product
[
@dept
=
$
dept
]/
name
}
Results
<WMN>
<name
language=
"en"
>
Fleece Pullover</name>
</WMN>
<ACC>
<name
language=
"en"
>
Floppy Sun Hat</name>
<name
language=
"en"
>
Deluxe Travel Bag</name>
</ACC>
<MEN>
<name
language=
"en"
>
Cotton Dress Shirt</name>
</MEN>
In the results, the department names are now element names. The second expression in curly braces returns all the name
elements for products in that department.
Computed constructors are also useful to recursively process elements regardless of their names. Example 5-10 showed how to add an id
attribute to a product
element. Suppose you wanted to add an id
attribute to every element in a document, regardless of its name. It is necessary to use computed constructors for this, because you will not know the name of the constructed elements in advance. “Copying Input Elements with Modifications” shows some further examples of using computed constructors to generically handle elements with any name.