Issue quitting out of JSON Iteration
I had a challenge recently where I was asked to parse and iterate JSON to find a patient that has a forename = "Bill" and try to make the function that iterates through the code re-usable.
Being working in Healthcare there isn't much experience or demand for parsing JSON, as most of my work is HL7.
I took this on using the documentation and worked out a way to parse the JSON (see posts below for code and source data).
I've worked out how to iterate through the fields and repeating elements, however I'm having an issue quitting out upon finding a patient with the forename Bill.
Because the JSONIterator function I created is called for each level of the JSON record, I tried putting in a variable that is set to true upon finding "Bill" and is reviewed at the beginning of the function and if true quit out, but I'm not succesful.
ClassMethod JsonFile(SearchKey As %String = "forename", SearchVal As %String = "Bill")
{
Set dir = "C:\Users\kanga\OneDrive\Documents\Coding\Names_JSON.txt"
Set Srchkey = SearchKey
Set SrchVal = SearchVal
Set found = 0
If (##class(%File).Exists(dir)'=1) {
Write !, "Unable to open file: "_dir
Quit
} ElseIf ((Srchkey="")||(SrchVal="")) {
Write !, "SearchKey or SearchVal are Null, please enter these for this search to work"
} Else {
Set file = ##class(%FileCharacterStream).%New()
Set sc = file.LinkToFile(dir)
Set jsoninput = [].%FromJSON(file)
Set objpath = "jsonobj"
Set currobj = ..JSONIterator(jsoninput,objpath,Srchkey,SrchVal,found)
}
}
ClassMethod JSONIterator(jsonobj As %DynamicAbstractObject, objpath, SearchKey As %String, SearchVal As %String, found As %Integer)
{
If found=1 {Return found}
#dim iterator As %Iterator.Array
Set iterator = jsonobj.%GetIterator()
While iterator.%GetNext(.key, .value) {
Set type = jsonobj.%GetTypeOf(key)
Set jsonobj.value = value
If ((key=SearchKey)&&(value=SearchVal)) {
Write !, "Searchkey found: "_SearchKey_" and SearchVal found: "_SearchVal_" at path: "_objpath
Set found = $INCREMENT(found)
Write !,"Found: "_found
Return found
}
If $CLASSNAME(jsonobj) = "%Library.DynamicArray" {
Set newPath = objpath _ ".%GetAt(" _ key _ ")"
} Else {
If $ZNAME(key, 6) = 1 {
Set newPath = objpath _ "." _ key
} Else {
Set newPath = objpath _ ".""" _ key _ """"
}
}
If $ISOBJECT(value) {
Do ..JSONIterator(value,newPath,.SearchKey,.SearchVal,found)
}
}
Return found
}
Source JSON:
{
"patient": [
{
"age": 51,
"name": {
"forename": "Bill",
"surname": "Brighthouse"
}
},
{
"age": 2,
"name": {
"forename": "Dickie",
"surname": "Bird"
}
}
]
}
Hi Stuart,
I see in this line: set jsoninput = [].%FromJSON(file)
I am not sure this is correct. Can you try set jsoninput = {}.%FromJSON(file) ?
Thanks for the advice @Vitaly Furman, I used square bracket syntax as I was iterating over an array.
I did try my code using {} brackets and my code still isn't ending the nested IF loops upon finding the field and value that match.
Regards
Stuart
You can also just call:
set jsoninput = {}.%FromJSON(dir)
where dir is a filename.
You need to make sure "found" is being updated when the recursive call exits. This can either be done by reference (passing in .found) or simply as a return value (set found instead of do). It is always going through the full loop and never exiting early because once the value is found, it is never passed back up as being found. This means that the loop just continues on. Here are two examples of these solutions
do ..JSONIterator(value,newPath,.SearchKey,.SearchVal,.found)
}
set found= ..JSONIterator(value,newPath,.SearchKey,.SearchVal,found)
}
Thank you @Peter.Steiwer that nailed it swizzling .found.