Reputation: 1546
My document is in XML and I need to construct a JSON format based on this document. First I created a query returning the right XML structure but when I convert this query to return JSON, I have a For Return problem.
Error: [1.0-ml] XDMP-UNDVAR: (err:XPST0008) Undefined variable $cat
Original query:
let $tree :=
<tree>{for $cat in distinct-values($doc//ns:category/@name)
return <cat name="{$cat}">
{ for $var in $doc//ns:category[@name = $cat]//ns:variable/@name
return <var name="{$var}">
}
</var>
}
</cat>
}
</tree>
return $tree
Data:
<category name="Catname">
<variable name="Varname">
<segment name="Seg1">9</segment>
<segment name="Seg2">33</segment>
<segment name="Seg3">32</segment>
<segment name="Seg4">22</segment>
</variable>
<variable name="Vartwo">
<segment name="Seg2one">1</segment>
<segment name="Seg2two">2</segment>
</variable>
</category>
<category> ....
New Query:
let $tree :=
json:array(
<json:array xmlns:json="http://marklogic.com/xdmp/json"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<json:value xsi:type="xs:string">
{for $cat in distinct-values($doc//ns:category/@name) return $cat}
</json:value>
<json:array>
{for $var in $doc//ns:category[@name = $cat]//ns:variable/@name return $var}
</json:array>
</json:array>
)
return $tree
Expected Output
{"Catname": ["Varname": ["Seg1", "Seg2", "Seg3"], "Vartwo": ["Seg2one", "Seg2two"]]},
{"Cat2" ... }
Upvotes: 1
Views: 263
Reputation: 3056
You're getting that error because the variable $cat
is not in scope inside of the nested <json:array/>
element. It's defined in the previous scope, and, therefore, not available.
I'm not sure what JSON structure you want to create, but it's definitely awkward to use that XML serialization to create JSON. I prefer an approach like this:
Update: The provided JSON structure isn't valid; I've taken the liberty of converting the category property to an object. These XQuery samples will output JSON as follows:
{"Catname":{"Vartwo":["Seg2one","Seg2two"], "Varname":["Seg1","Seg2","Seg3","Seg4"]}}
This approach uses the functional maps API:
let $map :=
map:new(
for $cat in distinct-values($doc//ns:category/@name)
return
map:entry($cat,
for $var in $doc//ns:category[@name = $cat]//ns:variable
let $name := $var/@name/fn:string()
return map:entry($name, json:to-array($var/ns:segment/@name/fn:string())) ))
return xdmp:to-json($map)
And here's the same code, using the procedural maps API:
let $map := map:map()
let $_ :=
for $cat in distinct-values($doc//ns:category/@name)
let $cat-map := map:map()
let $_ :=
for $var in $doc//ns:category[@name = $cat]//ns:variable
let $name := $var/@name/fn:string()
return map:put($cat-map, $name, json:to-array($var/ns:segment/@name/fn:string()))
return map:put($map, $cat, $cat-map)
return xdmp:to-json($map)
The key takeaways from this approach are
This code returns the following JSON from your sample XML: {"Catname":["Varname"]}
, which doesn't seem very useful. I'll update once you've added the output you want to see.
Upvotes: 2
Reputation: 504
You miss the context in the second for loop
You probably want to do something like this:
{
for $cat in distinct-values($doc//ns:category/@name) return (
<json:value xsi:type="xs:string">
{$cat}
</json:value>
<json:array>
{for $var in $doc//ns:category[@name = $cat]//ns:variable/@name return <json:value>{$var}</json:value>}
</json:array>
)
}
Upvotes: 2