Skip to content

Instantly share code, notes, and snippets.

@tvnpraveen
Last active July 7, 2017 16:43
Show Gist options
  • Save tvnpraveen/8e178733cf08192c4c2eb37aa9638ce0 to your computer and use it in GitHub Desktop.
Save tvnpraveen/8e178733cf08192c4c2eb37aa9638ce0 to your computer and use it in GitHub Desktop.
XML to JSON Conversion query
xquery version "1.0-ml";
(:~ This library is a simple XML to JSON conversion utility.
: ****This library does not support attributes in XML
:
:
:)
module namespace xml2json = "http://lib/utils/xml2json";
import module namespace functx = "http://www.functx.com" at "/MarkLogic/functx/functx-1.0-nodoc-2007-01.xqy";
(:~
External variables
:)
declare variable $booleanElements := map:map();
declare variable $integerElements := map:map();
declare variable $arrayElements := map:map();
(:~
Internal variables
:)
declare variable $elementMap := map:map();
declare variable $autoArray := fn:false();
(: ######## JSON STRING FORMAT FUNCTIONS : BEGIN :)
(:~
This function wraps a given text with double quotes
@param val - Input string
@return xs:string - Returns string wrapped in quotes
:)
declare private function xml2json:wrapQuotes($val as xs:string) as xs:string {
""""||$val||""""
};
(:~
This function joins JSON key:value strings seperated by comma and wraps them in braces
@param vals - Input JSON key:value strings
@return xs:string - Returns a JSON object type string
:)
declare private function xml2json:combineAndWrapInBraces($vals as xs:string*) as xs:string {
"{"||fn:string-join($vals,",")||"}"
};
(: ######## JSON STRING FORMAT FUNCTIONS : END :)
(: ######## DATA FORMAT FUNCTIONS : BEGIN :)
(:~
This function checks a given input nodeName against a given set of element maps for boolean or integer
and returns the value as is OR wrapped with quotes if there is no match
@param nodeName - Node name to match
@param text - Node value
@return xs:string - Returns the node value depending on the data type
:)
declare private function xml2json:getTextFromMapping($nodeName as xs:string, $text as xs:string) as xs:string{
let $val := fn:normalize-space($text)
return
if (map:contains($booleanElements, $nodeName) or map:contains($integerElements, $nodeName)) then
$val
else (xml2json:wrapQuotes($val))
};
(: ######## DATA FORMAT FUNCTIONS : END :)
(: ######## MAIN RECURSIVE FUNCTION : BEGIN :)
(:~
This function is the main recursive function which basically evaluates all the given node and all its children nodes in a recursive method
@param - node - XML node to be parsed as JSON
@return - xs:string - JSON string
:)
declare private function xml2json:convertXMLToJson($node as node()) as xs:string? {
let $ln := fn:local-name($node)
return
typeswitch($node)
case element() return
(
(: Logic for array element check :)
let $arrayElementCheck :=
if($autoArray) then
if ( fn:count($node/../element()[fn:local-name() eq $ln]) gt 1) then fn:true() else fn:false()
else(
map:contains($arrayElements,$ln)
)
return
if($arrayElementCheck) then
(
if(fn:not($node/node())) then
xml2json:wrapQuotes($ln)||" : []"
else(
let $parentPosition := fn:generate-id($node/..)
let $nodePath := xs:string($parentPosition)||$ln
return
if(fn:not(map:contains($elementMap, $nodePath))) then
(
let $_ := map:put($elementMap, $nodePath, $nodePath)
return
xml2json:wrapQuotes($ln)||" : ["||
fn:string-join(
(
let $childNodes := $node/../element()[fn:local-name(.) eq $ln]
for $childNode at $pos in $childNodes
return
(
if($childNode/node() instance of text()) then
xml2json:convertXMLToJson($childNode/text())
else (
xml2json:combineAndWrapInBraces(xml2json:convertXMLToJson($childNode/node()))
)
)
)
,
","
)
||"]"
)
else()
)
)
else if($node/text()) then
xml2json:wrapQuotes($ln)||" : "||xml2json:getTextFromMapping($ln, $node/text())
else if($node/element()) then
xml2json:wrapQuotes($ln)||" : "||xml2json:combineAndWrapInBraces(xml2json:convertXMLToJson($node/node()))
else if(fn:not($node/node())) then
xml2json:wrapQuotes($ln)||" : null"
else()
)
case text() return
xml2json:getTextFromMapping(fn:local-name($node/..),$node)
default return
()
};
(: ######## MAIN RECURSIVE FUNCTION : END :)
(:~ ######## ACCESS FUNCTIONS WITH NO ADDITIONAL MAP INPUT ########## :)
(:~
This function will convert XML document from an URI to JSON.
The array elements will be automatically parsed depending on the input
@param doc - XML document to be parsed
@return xs:string - Returns a JSON string
:)
declare function xml2json:getJSONFromXMLURI($URI as xs:string){
xml2json:getJSONFromXMLURI($URI, (), (), ())
};
(:~
This function will convert XML document to JSON.
The array elements will be automatically parsed depending on the input
@param doc - XML document to be parsed
@return xs:string - Returns a JSON string
:)
declare function xml2json:getJSONFromXMLDoc($doc as document-node()) as xs:string {
xml2json:getJSONFromXMLDoc($doc, (), (), ())
};
(:~
This function will convert XML node to JSON.
The array elements will be automatically parsed depending on the input
@param doc - XML document to be parsed
@return xs:string - Returns a JSON string
:)
declare function xml2json:getJSONFromXMLNode($node as node()) as xs:string {
xml2json:getJSONFromXMLNode($node, (), (), ())
};
(:~ ######## ACCESS FUNCTIONS WITH ONLY ARRAY ELEMENTS MAP INPUT ##########
Example -
Sample XML :
<test>
<firstElement>
<firstChildElement>
firstChildText
</firstChildElement>
<secondChildElement>
secondChildText1
</secondChildElement>
<secondChildElement>
secondChildText2
</secondChildElement>
</firstElement>
<firstElement>
<firstChildElement>
firstChildText1
</firstChildElement>
</firstElement>
</test>
inputArrayElements :
map:map(
<map:map xmlns:map="http://marklogic.com/xdmp/map" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<map:entry key="firstElement">
<map:value xsi:type="xs:string">firstElement</map:value>
</map:entry>
<map:entry key="secondChildElement">
<map:value xsi:type="xs:string">secondChildElement</map:value>
</map:entry>
</map:map>)
JSON Output From above input:
{
"test": {
"firstElement": [{
"firstChildElement": "firstChildText",
"secondChildElement": ["secondChildText1",
"secondChildText2"]
},
{
"firstChildElement": "firstChildText1"
}]
}
}
:)
(:~
This function will convert XML document to JSON with an additional input for arrayElements
@param doc - XML document to be parsed
@param inputArrayElements - This is a Map of array elements to be parsed as JSON arrays. Refer to above example
@return xs:string - Returns a JSON string
:)
declare function xml2json:getJSONFromXMLURI($URI as xs:string,
$inputArrayElements as map:map){
xml2json:getJSONFromXMLURI($URI, $inputArrayElements, (), ())
};
(:~
This function will convert XML document to JSON with an additional input for arrayElements
@param doc - XML document to be parsed
@param inputArrayElements - This is a Map of array elements to be parsed as JSON arrays. Refer to above example
@return xs:string - Returns a JSON string
:)
declare function xml2json:getJSONFromXMLDoc($doc as document-node(),
$inputArrayElements as map:map){
xml2json:getJSONFromXMLDoc($doc, $inputArrayElements, (), ())
};
(:~
This function will convert XML document to JSON with an additional input for arrayElements
@param doc - XML document to be parsed
@param inputArrayElements - This is a Map of array elements to be parsed as JSON arrays. Refer to above example
@return xs:string - Returns a JSON string
:)
declare function xml2json:getJSONFromXMLNode($node as node(),
$inputArrayElements as map:map){
xml2json:getJSONFromXMLNode($node, $inputArrayElements, (), ())
};
(:~ ######## ACCESS FUNCTIONS WITH ARRAY ELEMENTS MAP, BOOLEAN ELEMENTS MAP, AND INTEGER ELEMENTS MAP INPUT ##########
Example -
Sample XML :
<test>
<firstElement>
<firstChildElement>
firstChildText
</firstChildElement>
<firstChildBooleanElement>
false
</firstChildBooleanElement>
<secondChildElement>
secondChildText1
</secondChildElement>
<secondChildElement>
secondChildText2
</secondChildElement>
</firstElement>
<firstElement>
<firstChildElement>
firstChildText1
</firstChildElement>
<firstChildIntegerElement>
false
</firstChildIntegerElement>
</firstElement>
</test>
inputArrayElements :
map:map(
<map:map xmlns:map="http://marklogic.com/xdmp/map" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<map:entry key="firstElement">
<map:value xsi:type="xs:string">firstElement</map:value>
</map:entry>
<map:entry key="secondChildElement">
<map:value xsi:type="xs:string">secondChildElement</map:value>
</map:entry>
</map:map>)
inputBooleanElements :
map:map(
<map:map xmlns:map="http://marklogic.com/xdmp/map" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<map:entry key="firstChildBooleanElement">
<map:value xsi:type="xs:string">firstElement</map:value>
</map:entry>
</map:map>)
inputIntegerElements :
map:map(
<map:map xmlns:map="http://marklogic.com/xdmp/map" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<map:entry key="firstChildIntegerElement">
<map:value xsi:type="xs:string">firstElement</map:value>
</map:entry>
</map:map>)
JSON Output From above input:
{
"test": {
"firstElement": [{
"firstChildElement": "firstChildText",
"secondChildElement": ["secondChildText1",
"secondChildText2"]
},
{
"firstChildElement": "firstChildText1"
}]
}
}
:)
(:~
This function will convert XML document from an URI to JSON with an additional input for arrayElements, booleanElements, integerElements
@param doc - XML document to be parsed
@param inputArrayElements - This is a Map of array elements to be parsed as JSON arrays
@param inputBooleanElements - This is a Map of boolean type elements to be parsed as JSON boolean type
@param inputIntegerElements - This is a Map of integer elements to be parsed as JSON boolean type
@return xs:string - Returns a JSON string
:)
declare function xml2json:getJSONFromXMLURI($URI as xs:string,
$inputArrayElements as map:map?,
$inputBooleanElements as map:map?,
$inputIntegerElements as map:map?) as xs:string{
xml2json:getJSONFromXMLDoc(fn:doc($URI), $inputArrayElements, $inputBooleanElements, $inputIntegerElements)
};
(:~
This function will convert XML document to JSON with an additional input for arrayElements, booleanElements, integerElements
@param doc - XML document to be parsed
@param inputArrayElements - This is a Map of array elements to be parsed as JSON arrays
@param inputBooleanElements - This is a Map of boolean type elements to be parsed as JSON boolean type
@param inputIntegerElements - This is a Map of integer elements to be parsed as JSON boolean type
@return xs:string - Returns a JSON string
:)
declare function xml2json:getJSONFromXMLDoc($doc as document-node(),
$inputArrayElements as map:map?,
$inputBooleanElements as map:map?,
$inputIntegerElements as map:map?) as xs:string{
xml2json:getJSONFromXMLNode($doc/node(), $inputArrayElements, $inputBooleanElements, $inputIntegerElements)
};
(:~
This function will convert XML node to JSON with an additional input for arrayElements, booleanElements, integerElements
@param doc - XML document to be parsed
@param inputArrayElements - This is a Map of array elements to be parsed as JSON arrays
@param inputBooleanElements - This is a Map of boolean type elements to be parsed as JSON boolean type
@param inputIntegerElements - This is a Map of integer elements to be parsed as JSON boolean type
@return xs:string - Returns a JSON string
:)
declare function xml2json:getJSONFromXMLNode($node as node(),
$inputArrayElements as map:map?,
$inputBooleanElements as map:map?,
$inputIntegerElements as map:map?) as xs:string{
let $_ :=
(
if(map:count($inputArrayElements) ge 1) then
xdmp:set($arrayElements, $inputArrayElements)
else (xdmp:set($autoArray, fn:true()))
,
if(map:count($inputBooleanElements) ge 1) then
xdmp:set($booleanElements, $inputBooleanElements)
else()
,
if(map:count($inputIntegerElements) ge 1) then
xdmp:set($integerElements, $inputIntegerElements)
else()
)
return
xml2json:combineAndWrapInBraces(xml2json:convertXMLToJson($node))
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment