Question
Stuart Byrne · Mar 14

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
}
Product version: HealthShare 2017.2
$ZV: Cache for Windows (x86-64) 2017.2.2 (Build 865_0_18321U) Tue Jul 3 2018 17:52:49 EDT
00
2 0 6 146
Log in or sign up to continue

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

if $isObject(value) {
             do ..JSONIterator(value,newPath,.SearchKey,.SearchVal,.found)
         }

if $isObject(value) {
             set found= ..JSONIterator(value,newPath,.SearchKey,.SearchVal,found)
         }