Search code examples
marklogic

Marklogic how to create a document from a map


I need to create a specificly formatted document from a map

I have the following code:

declare function local:buid-map-doc(
  $wijk as xs:string,
  $wm as map:map) as element()
{  
  let $a := for $k in map:keys($wm)
              let $v := map:get($wm,$k)
              return element x {$v}
  return <y>{$a}</y>
};

let $wijk := "101101"
let $wm := map:map()
let $p := map:put($wm, "cat1:::k1",45683)
let $p := map:put($wm, "cat1:::k2",123)
let $p := map:put($wm, "cat2:::k2",123)

return  local:buid-map-doc($wijk,$wm)

gives:

<y>
  <x>123</x>
  <x>123</x>
  <x>45683</x>
</y>

but i want to have the map:keys i the element names ... if I do this:

declare function local:buid-map-doc(
  $wijk as xs:string,
  $wm as map:map) as element()
{  
  let $a := for $k in map:keys($wm)
              let $v := map:get($wm,$k)
              return element {$k} {$v}
  return <y>{$a}</y>
};

let $wijk := "101101"
let $wm := map:map()
let $p := map:put($wm, "cat1:::k1",45683)
let $p := map:put($wm, "cat1:::k2",123)
let $p := map:put($wm, "cat2:::k2",123)

return  local:buid-map-doc($wijk,$wm)

then i get error like:

[1.0-ml] XDMP-QNAMELEXFORM: let $s := fn:QName("http://www.example.com/example", "k") -- Invalid lexical form for QName

ADDED:

Ideally I would like to get output like this, potentially with deeper nesting as well:

<results>
    <cat1>
        <k1>45683</k1>
        <k2>123</k2>
    </cat1>
    <cat2>
        <k2>123</k2>
    </cat2>
</results>

Solution

  • The problem with your code is that your map keys are not valid QNames. So, the actual error id is correct, but it is indeed odd that it reports the wrong code with it. I'll make sure it gets reported at MarkLogic.

    The solution is quite simple, don't use colons in the map:keys, or use only one, and have the part before match with a known namespace prefix. E.g. this would work:

    declare function local:buid-map-doc(
      $wijk as xs:string,
      $wm as map:map) as element()
    {  
      let $a := for $k in map:keys($wm)
                let $v := map:get($wm,$k)
                return element {$k} {$v}
      return <y>{$a}</y>
    };
    
    let $wijk := "101101"
    let $wm := map:map()
    let $p := map:put($wm, "cat1_k1",45683)
    let $p := map:put($wm, "cat1_k2",123)
    let $p := map:put($wm, "cat2_k2",123)
    
    return  local:buid-map-doc($wijk,$wm)
    

    ADDED:

    In case the triple-colon actually indicates hierarchy, you best convert the flattened maps into nested maps first. Converting nested maps to nested XML is pretty straight-forward. Here a rough implementation to build nested maps:

    declare function local:nest-keys($nested-map, $keys, $value) {
      let $key := $keys[1]
      let $remainder := $keys[position() > 1]
      return
      if ($key) then
        let $_ :=
          if (not(map:contains($nested-map, $key))) then
            map:put($nested-map, $key, map:map())
          else()
        let $key-map :=
          map:get($nested-map, $key)
        return
          if ($remainder) then
            local:nest-keys($key-map, $remainder, $value)
          else
            map:put($nested-map, $key, $value)
      else
        ()
    };
    
    let $map := map:map(
    <map:map xmlns:map="http://marklogic.com/xdmp/map">
      <map:entry>
        <map:key>cat1:::var1:::seg1</map:key>
        <map:value>waarde1</map:value>
      </map:entry>
      <map:entry>
        <map:key>cat1:::var1:::seg2</map:key>
        <map:value>waarde2</map:value>
      </map:entry>
      <map:entry>
        <map:key>cat1:::var2:::seg1</map:key>
        <map:value>waarde3</map:value>
      </map:entry>
      <map:entry>
        <map:key>cat1:::var2:::seg2</map:key>
        <map:value>waarde4</map:value>
      </map:entry>
      <map:entry>
        <map:key>cat2:::var1:::seg1</map:key>
        <map:value>waarde5</map:value>
      </map:entry>
    </map:map>
    )
    let $nested-map := map:map()
    let $_ :=
      for $key in map:keys($map)
      let $keys := tokenize($key, ":::")
      return
        local:nest-keys($nested-map, $keys, map:get($map, $key))
    return $nested-map
    

    HTH!