While writing some code for the Exchange Rate Provider class, I ran across an issue where some exchange rate provider APIs only gave JSON output instead of XML. I found very little in Dynamics AX that would let me deserialize JSON. The only alternative was to manually parse the data based on the structure provided at the time.
I didn't like that approach. Manually parsing data like that can be a nightmare. The only tools I know of in Dynamics AX are the XML tools. With that in mind, I decided to write a JSON to XML converter.
classdeclaration
This file contains 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
class JSON2XML | |
{ | |
BinData binData; | |
} |
loadFromFile
This file contains 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
//load the JSON data from a file | |
public str loadFromFile(FilenameOpen _file) | |
{ | |
FileIOPermission _perm = new FileIOPermission(_file,"r"); | |
binData = new binData(); | |
_perm.assert(); | |
binData.loadFile(_file); | |
return binData.getStrData(); | |
} |
convert2xml
This file contains 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
//rudimentry method for converting JSON to XML | |
public str convert2xml(str _json) | |
{ | |
#define.SPACE(32) | |
//remove whitespace from JSON string | |
str stripSpace(str _str) | |
{ | |
int i; | |
str tmpStr; | |
boolean inquote; | |
for(i=1;i<=strLen(_str);i++) | |
{ | |
if (subStr(_str,i,1) == '"') | |
{ | |
inquote=true; | |
}else | |
{ | |
inquote = false; | |
} | |
if (char2num(_str,i) <= #SPACE && !inquote) | |
{ | |
continue; | |
} | |
tmpStr = tmpStr + subStr(_str,i,1); | |
} | |
return tmpStr; | |
} | |
//add header to xml | |
xml = '<?xml version="1.0" encoding="UTF-8"?>'; | |
xml = xml + "<root>"; | |
//convert | |
this.convert("root",stripSpace(_json)); | |
return xml; | |
} |
convert
This file contains 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
//recursive method to parse JSON string and construct XML. | |
container convert(str _tag,str _str) | |
{ | |
int i = strLen(_str); | |
str tmpStr = _str; | |
str tagStr,origTagStr; | |
str convStr; | |
int pos; | |
int quotePos; | |
int retval; | |
boolean expand; | |
boolean inquote; | |
boolean invalue; | |
pos = 1; | |
while (pos<=i) | |
{ | |
switch(subStr(tmpStr,pos,1)) | |
{ | |
//assign end tag and pop if not in quotes | |
case ',': | |
if (!inquote) | |
{ | |
if (invalue) | |
{ | |
xml = xml + subStr(tmpStr,quotePos,pos- quotePos); | |
invalue = false; | |
} | |
xml = xml + "</" + _tag + ">"; | |
return [pos,true]; | |
} | |
pos +=1; | |
break; | |
//beginning of a tag if not in quotes, push json element, value | |
case '{': | |
if (invalue) | |
{ | |
xml = xml + subStr(tmpStr,quotePos,pos - quotePos); | |
inValue = false; | |
} | |
pos +=1; | |
if (!inquote) | |
{ | |
[tagStr,origTagStr] = this.getTag(subStr(tmpStr,pos,strLen(tmpStr)-pos)); | |
xml = xml + "<" + tagStr + ">"; | |
pos += strLen(origTagStr) + 2; | |
convStr = subStr(tmpStr,pos,strLen(tmpStr)-(pos-1)); | |
[retval,expand] = this.convert(tagStr,convStr); | |
pos += retval; | |
//if we are going to expand, then poke a '{' to force another nesting tag | |
if (expand) | |
{ | |
pos -=1; | |
tmpStr = strPoke(tmpStr,"{",pos); | |
} | |
} | |
break; | |
//ending of the tag if not in quotes, pop json element | |
case '}': | |
if (invalue) | |
{ | |
xml = xml + subStr(tmpStr,quotePos,pos -quotePos); | |
invalue = false; | |
} | |
//pos +=1; | |
if (!inquote) | |
{ | |
xml = xml + "</" + _tag + ">"; | |
} | |
return [pos,false]; | |
//begin array if not in quote and push json element, value pairs | |
case '[': | |
if (invalue) | |
{ | |
xml = xml + subStr(tmpStr, quotePos, pos - quotePos); | |
invalue = false; | |
} | |
pos +=1; | |
if (!inquote) | |
{ | |
tagStr = 'element'; | |
xml = xml + "<" + tagStr + ">"; | |
convStr = subStr(tmpStr,pos,strLen(tmpStr)-(pos-1)); | |
[retval,expand] = this.convert(tagStr,convStr); | |
pos += retval; | |
if (expand) | |
{ | |
pos -=1; | |
tmpStr = strPoke(tmpStr,"[",pos); | |
} | |
} | |
break; | |
//end array, and pop json element | |
case ']': | |
if (invalue) | |
{ | |
xml = xml + subStr(tmpStr,quotePos, pos - quotePos); | |
invalue = false; | |
} | |
pos +=1; | |
if (!inquote) | |
{ | |
xml = xml + "</element>"; | |
} | |
return [pos, false]; | |
//begin string type | |
case '"': | |
pos +=1; | |
if (inquote) | |
{ | |
inquote = false; | |
//add value without quotation marks if they exist | |
xml = xml + strReplace(subStr(tmpStr,quotePos,pos-quotePos),'"',''); | |
quotePos=0; | |
}else | |
{ | |
inquote = true; | |
quotePos = pos-1; | |
} | |
break; | |
//parse the value of the element,value pair | |
case ':': | |
pos +=1; | |
if (inquote || invalue) | |
{ | |
break; | |
} | |
//we might have a value other than a string, we will capture all the characters | |
//and store it in the xml string as a string without quotes. | |
if (subStr(tmpStr,pos,1) !='"') | |
{ | |
quotePos = pos; | |
invalue = true; | |
} | |
break; | |
default: | |
pos +=1; | |
break; | |
} | |
} | |
if (invalue) | |
{ | |
xml = xml + subStr(tmpStr,quotePos,pos -quotePos); | |
} | |
xml = xml + "</" + _tag + ">"; | |
return [pos-1,false]; | |
} |
getTag
This file contains 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
//get tag from JSON string | |
container getTag(str _tagStr) | |
{ | |
str tmpStr = subStr(_tagStr,1,strScan(_tagStr,":",1,strLen(_tagStr))-1); | |
str retStr = strReplace(tmpStr,'"',''); | |
//XML does not allow the first characer of the tag name to be anything other than a alphabetic character | |
if (!strFind(retStr,"ABCDEFGHIJKLMNOPQRSTUVWXYZ",1,1)) | |
{ | |
return ["str_" + retStr,retStr]; | |
} | |
return [retStr,retStr]; | |
} |