Thijs
Thijs

Reputation: 1546

Constructing JSON

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

Answers (2)

joemfb
joemfb

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

  • construct arrays from XQuery sequences using json:to-array()
  • construct objects from maps using xdmp:to-json()

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

prker
prker

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

Related Questions