Reputation: 9063
I am trying to add an attribute to an tag in XQuery that corresponds to its position, but only concerning siblings of the same tag name. Specifically, items in a list. So here's 2 potential outputs of the query:
<list>
<title>foo</title>
<item>bar1</item>
<item>bar2</item>
</list>
<list>
<item>bar1</item>
<item>bar2</item>
</list>
And for both, here's the desired outcome when adding the attribute:
<list>
<title>foo</title>
<item position="1">bar1</item>
<item position="2">bar2</item>
</list>
<list>
<item position="1">bar1</item>
<item position="2">bar2</item>
</list>
The current query I'm using is:
count($item/preceding-sibling::*)+1
But then the count includes every tag, not just item
ones. So in the example above with the title tag, it counts that, so I get:
<list>
<title>foo</title>
<item position="2">bar1</item>
<item position="3">bar2</item>
</list>
I've tried a few things, such as subtracting the count of all children of the list that don't have the item
tag name, such as:
count($item/preceding-sibling::*)+1-count($list/*[local-name() != 'item'])
and a bunch of other similar methods but to no avail. How can I achieve this?
EDIT:
Here's a contextual example of how I'm doing it. Input data:
<LIST1>
<TITLE>REASON FOR REVISION</TITLE>
<L1ITEM KEY="L1I10254244923043">
<PARA>Summary - Background: Updated and added related references.</PARA>
</L1ITEM>
<L1ITEM KEY="L1I20300231297755">
<PARA>Summary - Action: Updated to add more work and new groups and configurations.</PARA>
</L1ITEM>
</LIST1>
Then I pass the LIST1 root element into this function:
declare function local:listx($listx as element()){
<list level="{substring(name($listx),5)}">
{
for $lxitem in $listx/*
return if (name($lxitem) = "TITLE") then (
<title>{local:concatText($lxitem)}</title>
)
else local:extract($lxitem, "item", [
["position", count($lxitem/preceding-sibling::*)+1]
])
}
</list>
};
The concatText
function just joins all the text together with a space delimiter, no issues with that. The extract()
function is as follow:
declare function local:extract(
$root as element(), $tag as xs:string, $props as array(*)
) {
element {$tag}{
for $prop at $i in $props
where array:size($prop) != 0
return attribute {$props($i)(1)} {$prop($i)(2)},
for $child in $root/*
return switch(name($child))
case "TXTGRPHC" return local:plaintext($child)
case "PARA" return local:plaintext($child)
case ...
default return ()
}
};
As a side note, the way I add attributes here, with the whole 2D array $props
thing, only ever adds the first attribute, the rest are ignored. Though that's another problem.
Upvotes: 0
Views: 888
Reputation: 167541
As you use BaseX, you could use XQuery Update http://docs.basex.org/wiki/XQuery_Update, for instance
copy $items := <root>
<list>
<title>foo</title>
<item>bar1</item>
<item>bar2</item>
</list>
<list>
<item>bar1</item>
<item>bar2</item>
</list>
</root>
modify (
for $item in $items/list/item
return insert node attribute {'id'} {count($item/(., preceding-sibling::item))} into $item
)
return $items
Result is
<root>
<list>
<title>foo</title>
<item id="1">bar1</item>
<item id="2">bar2</item>
</list>
<list>
<item id="1">bar1</item>
<item id="2">bar2</item>
</list>
</root>
Upvotes: 2