Created
August 11, 2016 20:25
-
-
Save paxtonhare/0b61e46ca99a38ebcbef4551a1ba2838 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<export><workspace name="json-tree-walker"><query name="INSERT-DOCS" focus="false" listorder="1" taborder="1" active="true" database="12188229314415601684" server="3177585694450821129" database-name="Documents" server-name="App-Services" mode="xquery">(: Insert our Test Documents :) | |
(: a document with an object node at the root :) | |
xdmp:document-insert('/test.json', xdmp:unquote('{ | |
"string": "a string value", | |
"boolean-false": false, | |
"boolean-true": true, | |
"number-negative": -123, | |
"number-positive": 123, | |
"null": null, | |
"array": [ | |
"an", | |
"array", | |
"of", | |
"strings" | |
], | |
"array-of-objects": [ | |
{ | |
"an-object": "within-an-array" | |
}, | |
{ | |
"a-positive-number": 456 | |
} | |
], | |
"object": { | |
"another": "object" | |
} | |
}')), | |
(: a document with an array node at the root :) | |
xdmp:document-insert('/test2.json', xdmp:unquote('[ | |
{ | |
"first": "object" | |
}, | |
{ | |
"string": "a string value", | |
"boolean-false": false, | |
"boolean-true": true, | |
"number-negative": -123, | |
"number-positive": 123, | |
"null": null, | |
"array": [ | |
"an", | |
"array", | |
"of", | |
"strings" | |
], | |
"array-of-objects": [ | |
{ | |
"an-object": "within-an-array" | |
}, | |
{ | |
"a-positive-number": 456 | |
} | |
], | |
"array-of-numbers": [1, 2, 3], | |
"array-of-arrays": [ | |
[ | |
"sub-array1", | |
{ | |
"obj": 123 | |
}, | |
[ | |
"sub-sub-array1" | |
], | |
[ | |
"sub-sub-array2" | |
] | |
], | |
[ | |
"sub-array2" | |
], | |
[ | |
"sub-array3" | |
] | |
], | |
"object": { | |
"another": "object" | |
} | |
}, | |
{ | |
"last": "object" | |
} | |
]')), | |
(: a document with a number node at the root :) | |
xdmp:document-insert("/test3.json", number-node{ 5 }), | |
(: a document with a boolean node at the root :) | |
xdmp:document-insert("/test4.json", boolean-node{ fn:true() }), | |
(: a document with a null node at the root :) | |
xdmp:document-insert("/test5.json", null-node{ }), | |
(: a document with a text node at the root :) | |
xdmp:document-insert("/test6.json", text{ "a string" }), | |
(: an xml document just for grins :) | |
xdmp:document-insert("/test7.json", <this-is-a-dirty-trick/>)</query><query name="WALK-TEST" focus="true" listorder="2" taborder="2" active="true" database="12188229314415601684" server="3177585694450821129" database-name="Documents" server-name="App-Services" mode="xquery">xquery version "1.0-ml"; | |
declare namespace walker = "http://marklogic.com/ns/json-tree-walker"; | |
declare option xdmp:mapping "false"; | |
declare %private function walker:_walk-json($nodes as node()*, $o as json:object?, $visitor-func as function(*)) { | |
(: | |
: This closure handles some of the boilerplate of calling the visitor | |
: It merely exists to keep the rest of this function cleaner | |
:) | |
let $call-visitor := function($key, $value) { | |
let $response-map := map:new(( | |
map:entry("key", $key), | |
map:entry("value", $value) | |
)) | |
let $_ := $visitor-func($key, $value, $response-map) | |
return $response-map | |
} | |
for $n in $nodes | |
(: if this node has a name then turn it into a string. This is the json key. | |
: We use the ! operator to conditionally assign the string. If no node name exists | |
: then $key will be the empty sequence | |
: See https://developer.marklogic.com/blog/simple-mapping-operator for more details on ! | |
:) | |
let $key := fn:node-name($n) ! fn:string(.) | |
return | |
typeswitch($n) | |
case document-node() return | |
(: if it's a document node then start with the root node :) | |
walker:_walk-json($n/node(), $o, $visitor-func) | |
case object-node() return | |
(: create an in-memory json object :) | |
let $oo := json:object() | |
(: recursively walk every child of this object and put | |
them into our json object :) | |
let $_ := walker:_walk-json($n/node(), $oo, $visitor-func) | |
(: give our visitor function a chance to alter the key or value :) | |
let $r := $call-visitor($key, $oo) | |
let $key := map:get($r, "key") | |
let $value := map:get($r, "value") | |
return | |
(: any non-root object will have a name :) | |
if ($key and fn:exists($o) and fn:exists($value)) then | |
( map:put($o, $key, $value), $o ) | |
(: return the new object :) | |
else | |
$value | |
case array-node() return | |
(: create an in-memory json array to hold the values :) | |
let $aa := json:to-array(walker:_walk-json($n/node(), (), $visitor-func)) | |
(: give our visitor function a chance to alter the key or value :) | |
let $r := $call-visitor($key, $aa) | |
let $key := map:get($r, "key") | |
let $value := map:get($r, "value") | |
return | |
if (fn:exists($o) and fn:exists($value)) then | |
( map:put($o, $key, $value), $o ) | |
else | |
$value | |
case number-node() | | |
boolean-node() | | |
null-node() | | |
text() return | |
(: give our visitor function a chance to alter the key or value :) | |
let $r := $call-visitor($key, $n) | |
let $key := map:get($r, "key") | |
let $value := map:get($r, "value") | |
return | |
if (fn:exists($o) and fn:exists($value)) then | |
( map:put($o, $key, $value), $o ) | |
else | |
$value | |
(: this is our failsafe in case we missed something :) | |
default return | |
$n | |
}; | |
(:~ | |
: The public entry point for this library | |
: | |
: @param $nodes - the nodes to walk | |
: @param $visitor-func - a closure to call when a node is visited | |
: | |
: @return - the transformed json | |
:) | |
declare function walker:walk-json($nodes as node()*, $visitor-func as function(*)) | |
{ | |
walker:_walk-json($nodes, (), $visitor-func) | |
}; | |
for $doc in fn:doc() | |
return | |
walker:walk-json($doc, function($key, $value, $output) { | |
if ($value instance of json:object) then | |
(: upcase all object keys :) | |
map:put($output, "key", fn:upper-case($key)) | |
else if ($value instance of json:array) then | |
(: reverse every array :) | |
map:put($output, "value", json:to-array(fn:reverse(json:array-values($value)))) | |
else if ($value instance of number-node()) then | |
(: negate any number :) | |
map:put($output, "value", -$value) | |
else if ($value instance of boolean-node()) then | |
(: invert any boolean :) | |
map:put($output, "value", fn:not(xs:boolean($value))) | |
else if ($value instance of null-node()) then | |
(: omit nulls by returning the empty sequence for the value :) | |
map:put($output, "value", ()) | |
else | |
map:put($output, "value", $value) | |
})</query></workspace></export> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment