Reputation: 929
I am trying to convert the XML input given below to the output format using generic Xquery without calling any parent/child elements. If the parent has two or more recurring child then that should be grouped as an array.
XML:
<root>
<name>xxx</name>
<id>1</id>
<tasks>
<job>
<id>1</id>
<val>2</val>
</job>
<job>
<id>2</id>
<val>12</val>
</job>
<job>
<id>3</id>
<val>22</val>
</job>
</tasks>
</root>
Expected result:
<map>
<string key="name">xxx</string>
<string key="id">1</string>
<array key="tasks">
<map key="job">
<string key="id">1</string>
<string key="val">2</string>
</map>
<map key="job">
<string key="id">2</string>
<string key="val">12</string>
</map>
<map key="job">
<string key="id">3</string>
<string key="val">22</string>
</map>
</array>
</map>
XQuery I tried:
declare default element namespace "http://www.w3.org/2005/xpath-functions";
for $a in /*[1]
let $retval :=
<map>
{
for $z in $a//*
return if ($z/*/text()) then
(
<map>
{
for $zz in $z/child::*
return if ($zz/text()) then
(
<string key="{lower-case(name($zz))}">{data($zz)}</string>
)
else ()
}</map>
)
else ()
}
</map>
return $retval
Upvotes: 1
Views: 194
Reputation: 3445
You can try this
declare function local:create_structure($child)
{
for $input in $child
return
element {
(if ($input/*/*) then
'array'
else
if ($input/*) then
'map'
else
'string')
} {
attribute {'key'} {local-name($input)},
if ($input/*) then
local:create_structure($input/*)
else
$input/node()
}
};
let $Output := <map>{local:create_structure(/*)}</map>
return
$Output
See Transformation at https://xqueryfiddle.liberty-development.net/bFDbxkW/1
Upvotes: 0
Reputation: 239
Hi Please test attached code:
declare function local:testChild($in)
{
if($in/text())
then <string key="{local-name($in)}">{$in/node()}</string>
else if(not($in/*/text()))
then
<array key="{local-name($in)}">{for $in in $in/*
return local:testChild($in)
}</array>
else <map key="{local-name($in)}">{for $in in $in/*
return local:testChild($in)
}</map>
};
let $markup:=<root>
<name>xxx</name>
<id>1</id>
<tasks>
<job>
<id>1</id>
<val>2</val>
</job>
<job>
<id>2</id>
<val>12</val>
</job>
<job>
<id>3</id>
<val>22</val>
</job>
</tasks>
</root>
let $first:=<map>{for $in in $markup/*
return local:testChild($in)
}</map>
return $first
See transformation at https://xqueryfiddle.liberty-development.net/94hwphU
Upvotes: 2