Search code examples
jsonxmlxquerymarklogicmarklogic-10

Add element names dynamically to array-element-names configuration of json:transform-to-json() in Marklogic


I'm doing a cts:search and converting the result nodes to JSON using json:transform-to-json() function in MarkLogic 10.

Sample code:

let $config := json:config("custom")
let $response := cts:search(fn:doc(),$query)
return json:transform-to-json($response,$config)

The resultant XML nodes might have child elements with same name. I want these to be converted as array during conversion, so I need to fetch all the multiple child elements with same name and add the QName values as array-element-names config before the conversion.

The issue here is I won't be knowing the array child element names that would appear in the resultant XML nodes and also some results might have more number of child nodes(greater than 50 nodes).

Sample XML result node:

<meta>
      <id>draa066</id>
      <id>draa088</id>
      <xref rid="aff1" ref-type="aff"/>
      <xref rid="cor1" ref-type="corresp"/>
      <email>[email protected]</email>
</meta>

Expected JSON output:

{
  "meta": {
    "id": [
      "draa066",
      "draa088"
    ],
    "xref": [
      "",
      ""
    ],
    "email": "[email protected]"
  }
}

Please let me know on the possibility to do this.


Solution

  • I believe the following will work for you.

    Please note the example of 3 items on purpose. The middle one (no array) shows that the function signature of returning no results as a possibility alows the safe use of the array-element-names even when no result (because MarkLogic will not store map entries with no value)

    xquery version "1.0-ml";
    import module namespace json="http://marklogic.com/xdmp/json"
     at "/MarkLogic/json/json.xqy";
    
    declare function local:get-duplicate-element-names($doc) as xs:QName* {
      fn:distinct-values(for $node in $doc/meta/node()
        let $name := fn:node-name($node)
        where fn:count($doc/meta/node()[fn:node-name(.) = $name]) > 1
        return $name)
    };
    
    
    let $responses := (
      document{
        <meta>
              <id>draa066</id>
              <id>draa088</id>
              <xref rid="aff1" ref-type="aff"/>
              <xref rid="cor1" ref-type="corresp"/>
              <email>[email protected]</email>
        </meta>
      },
      document{  
        <meta>
              <id>draa088</id>
              <xref rid="aff1" ref-type="aff"/>
              <email>[email protected]</email>
        </meta>
      },
      document{
        <meta>
              <id>draa066</id>
              <xref rid="aff1" ref-type="aff"/>
              <xref rid="cor1" ref-type="corresp"/>
              <email>[email protected]</email>
        </meta>
      }
    )
    
    let $config := json:config("custom")
    
    return for $response in $responses
      return json:transform-to-json($response,$config=>map:with("array-element-names", local:get-duplicate-element-names($response)))
    

    Results:

       {
          "meta":{
             "id":[
                "draa066",
                "draa088"
             ],
             "xref":[
                {
                   "rid":"aff1",
                   "ref-type":"aff"
                },
                {
                   "rid":"cor1",
                   "ref-type":"corresp"
                }
             ],
             "email":"[email protected]"
          }
       }
       
       {
          "meta":{
             "id":"draa088",
             "xref":{
                "rid":"aff1",
                "ref-type":"aff"
             },
             "email":"[email protected]"
          }
       }
       
       {
          "meta":{
             "id":"draa066",
             "xref":[
                {
                   "rid":"aff1",
                   "ref-type":"aff"
                },
                {
                   "rid":"cor1",
                   "ref-type":"corresp"
                }
             ],
             "email":"[email protected]"
          }
       }