I'm not sure about what you mean with "unlimited amount of indexes"...

first, there is a limit of 255 for the parameters, this means you can have 

do ..setValue(key1,key2,...key254,value)

but you can't have 

 do ..setValue(key1,key2,...key254,key255,value)

second, if you meant to have many-many values (in sense of, we have to much data)

  do ..setValue(key1,value1)
  do ..setValue(key2,value2)
  // etc.

then, at some point you will reach the and of the available RAM (incl. swap/paging space.  Usually swap/paging space is much less then the space for the real data).

thirdly, if you meant variable number of indexes (in sense of variable number of parameters) then I can just refine Robert's solution to

ClassMethod setValue(params...) As %Status
{
   set base=$name(%session.Data)
   for i=1:1:params-1 { set base=$name(@base@(params(i)))
   set @base=params(params)
   quit 1
}

In case, you have both, variable number of indices and too much data then you could use a mix of the above with data placed in a temp file.

Somewhere, at the program start, let you point %sessionData to a temp file

   set %sessionData=$nam(^||SessionTemp)

and then consider this in the setValue method

ClassMethod setValue(params...) As %Status
{
   set bas=%sessionData
   ...
}

Of course, in your application, you have to change only those %session.Data(...) to  @%sessionData@(...)  which uses the "variable-big-data" part of %session.Data(). All other (the standard ones) stay unchanged.

I'm pretty sure, you intended to write

select COUNT (Arrival Time) FROM dbo.table where Arrival Time < DATEADD(ms, CONVERT(int,LEFT(1603173432000, 20)), '1970-01-01 00:00:00')

i.e. do not place propertynames under quote

By the way, I don't know, how many records you have in your table, but if you have thousands or millions of records, consider to compute constant things just one time!

DATEADD(ms, CONVERT(int,LEFT(1603173432000, 20)), '1970-01-01 00:00:00')

is a constant value, it's not neccessary to compute it for each record! Also, in this particular case, CONVERT() and LEFT() are also not needless, 1603173432000 is the value, and it is an integer.

As Robert said, XSLT... but as he indicated too, in this case XSLT would be an overkill.

But there is also a "forgotten" function, $locate() too for such cases! This function is almost never used in DC examples. Two or three lines of code, and you have the perfect solution... but for "simple" cases only. Beware of nested tags!

SetFixedValue(str,tag,value) Public
{
   set i=0, t="(?i)<"_tag_">[^<]+", s=$l(tag)+2
   while $locate(str,t,i,j,v) { set $e(str,j+s-$l(v),j-1)=value, i=j+s-$l(v)+$l(value) }
   quit str
}

It works like a charm...

set a="<Name>..."
write $$SetFixedValue^test(a,"rollno","***")  -->  <Name>ABC</Name><RollNo>***</RollNo><Name>XYZ</Name><RollNo>***</RollNo><Name>xyz</Name><RollNo>***</RollNo>

To say it simply, you did this:

set x=123
set x=456

and just  see 456 but not 123!

What you (probably) want to do is:

set x(1)=123
set x(2)=456

I added one more "patient item" to your JSON and a simple test class

Class DC.PR1 Extends %RegisteredObject
{ 
ClassMethod GetDataFromTestStream() [ PublicList = (myGuid, myId, myForename, mySurname, myDOB, nokName, nokRelation, nokTel, nokEmail) ]
{
   set xData=##class(%Dictionary.CompiledXData).%OpenId(..%ClassName(1)_"||Test")
   try { set obj={}.%FromJSON(xData.Data) }
   catch e { set obj="",err=e.Name }
   if 'obj { 
      write "Error: ",err,!
      quit ""
   }  

   // Put all properties in the corresponding SUBSCRIPTED variable
   //
   for i=0:1:obj.%Size()-1 { // loop over patients (top array)
      set patient=obj.%Get(i).patient

      set myGuid(i)=patient.guid
      set myId(i)=patient.id
      set myForename(i)=patient.forename
      set mySurname(i)=patient.surname
      set myDOB(i)=patient.dateOfBirth

      for j=0:1:patient.NOK.%Size()-1 { // loop over NOKs of the i-th patient
         set nok=patient.NOK.%Get(j)

         set nokName(i,j)=nok.NOKname
         set nokRelation(i,j)=nok.NOKrelationship
         set nokTel(i,j)=nok.telephone
         set nokEmail(i,j)=nok.email

         // instead of storing each property, you could
         // process them hier...
      }
   }
} 

XData Test [ MimeType = application/json ]
{
[
  {
    "patient":
      {
        "guid":"12345",
        "id":12345,
        "forename":"Joe",
        "surname":"Bloggs",
        "dateOfBirth":"2002-12-10T00:00:00Z",
        "NOK":[
                {
                   "NOKname":"Alison Bloggs",
                   "NOKrelationship":"Wife",
                   "telephone":"02081234567",
                   "email":"alison@bloggs.com"
                },
                {
                   "NOKname":"Peter Bloggs",
                   "NOKrelationship":"Father",
                   "telephone":"02081234567",
                   "email":"Peter@bloggs.com"
                }
              ]
      }
  },
  
  {
    "patient":
      {
        "guid":"212345",
        "id":212345,
        "forename":"John",
        "surname":"Blue",
        "dateOfBirth":"2003-12-10T00:00:00Z",
        "NOK":[
                {
                   "NOKname":"Aline Blue",
                   "NOKrelationship":"Wife",
                   "telephone":"032081234567",
                   "email":"aline@blue.com"
                },
                {
                   "NOKname":"Peter Blue",
                   "NOKrelationship":"Father",
                   "telephone":"032081234567",
                   "email":"Peter@blue.com"
                }
              ]
      }
  }

]
} 

}


Try it in a terminal session:

do ##class(DC.PR1).GetDataFromTestStream()

zwrite

The output  should be:

myDOB(0)="2002-12-10T00:00:00Z"
myDOB(1)="2003-12-10T00:00:00Z"
myForename(0)="Joe"
myForename(1)="John"
myGuid(0)=12345
myGuid(1)=212345
myId(0)=12345
myId(1)=212345
mySurname(0)="Bloggs"
mySurname(1)="Blue"
nokEmail(0,0)="alison@bloggs.com"
nokEmail(0,1)="Peter@bloggs.com"
nokEmail(1,0)="aline@blue.com"
nokEmail(1,1)="Peter@blue.com"
nokName(0,0)="Alison Bloggs"
nokName(0,1)="Peter Bloggs"
nokName(1,0)="Aline Blue"
nokName(1,1)="Peter Blue"
nokRelation(0,0)="Wife"
nokRelation(0,1)="Father"
nokRelation(1,0)="Wife"
nokRelation(1,1)="Father"
nokTel(0,0)="02081234567"
nokTel(0,1)="02081234567"
nokTel(1,0)="032081234567"
nokTel(1,1)="032081234567"

And don't forget, you may have more then one "patient" item in your JSON, so either consume the data in the inner loop oder place them in a two-dimensional array.

HTH

it's easy, without AM/PM

write $tr("abcd-ef-gh ij:kl:00","abcdefghijkl",202010011000),! // ODBC-Format
write $tr("gh.ef.abcd ij:kl:00","abcdefghijkl",202010011000),! // German-Format
write $tr("ef/gh/abcd ij:kl:00","abcdefghijkl",202010011000),! // US-Format

but if you need AM/PM, then the shortest way is using $zd()/$zth() functions

set datetime=202010011000
set tmp=$tr("abcdefgh ij:kl:00","abcdefghijkl",datetime) // or using $e(...)
set tmp=$e(datetime,1,8)_" "_$e(datetime,9,10)_":"_$e(datetime,11,12)_":00" write $zdt($zdth(tmp,3),1,3)

To make your (and others) life easier... here is a simple class to display a dynamic object (or dynamic array).

/// A general class for various helper functions
/// 
Class %zapi.utils Extends %RegisteredObject
{ /// Show an dynamic array or object
/// 
/// obj : the dynamic object (or array) you want to display
/// deep: max path depth, 0 (default) = display all
/// find: if given, (a part of) a property name
ClassMethod ShowObj(obj, deep = 0, find = "")
 {
   new %seen
   do ..shObj(obj,deep-1,find,"<obj>")
 }
ClassMethod shObj(obj, deep, find, path) [ Internal, Private ]
 {
   set path=path_"."
   set itr=obj.%GetIterator()
   while itr.%GetNext(.prop,.val) {
      set:obj.%IsA("%DynamicArray") prop="%Get("_prop_")"
      set prop=path_prop
      if $isobject(val) {
         if $data(%seen(+val)) { set val="same as --> "_%seen(+val)
         } else { set %seen(+val)=prop }
      }
      if $isobject(val),deep { do ..shObj(val,deep-1,find,prop)
      } else { write:prop[find prop,": ",val,! }
   }
 }
}

With our old pData,  use it as follows:

write pData 
[{"patient":{"guid":"12345","id":12345,"forename":"Joe","surname":"Bloggs","dateOfBirth":"2002-12-10T00:00:00Z"},"visit":[{"guid":45678,"date":"2020-01-10","reason":"other"},{"guid":45679,"date":"2020-01-11","reason":"routine"}],"
documentAttachments":[{"guid":"23432","id":152,"catergory":"notes"},{"guid":"23433","id":153,"catergory":"summary"}]}]

set obj={}.%FromJSON(pData)
do ##class(%zapi.utils).ShowObj(obj)

If you don't want to write each time a novel, you can create your own "show object" command too.
Insert the following line into %ZLANGC00 routine (if %ZLANG doesn't exists, create it):

ZSHO(obj, deep=0, find="") Public { do ##class(%zapi.utils).ShowObj(obj,deep,find) }

and Voila!, you got a short command to display dynamic objects

zsho obj              // display the whole object
zsho obj:2            // display up to  second nesting level
zsho obj::"visit"     // display all occurrences of property visit
zsho obj::"guid"      // display all occurrences of property guid

The outputs of the above commands are

USER>zsho obj
<obj>.%Get(0).patient.guid: 12345
<obj>.%Get(0).patient.id: 12345
<obj>.%Get(0).patient.forename: Joe
<obj>.%Get(0).patient.surname: Bloggs
<obj>.%Get(0).patient.dateOfBirth: 2002-12-10T00:00:00Z
<obj>.%Get(0).visit.%Get(0).guid: 45678
<obj>.%Get(0).visit.%Get(0).date: 2020-01-10
<obj>.%Get(0).visit.%Get(0).reason: other
<obj>.%Get(0).visit.%Get(1).guid: 45679
<obj>.%Get(0).visit.%Get(1).date: 2020-01-11
<obj>.%Get(0).visit.%Get(1).reason: routine
<obj>.%Get(0).documentAttachments.%Get(0).guid: 23432
<obj>.%Get(0).documentAttachments.%Get(0).id: 152
<obj>.%Get(0).documentAttachments.%Get(0).catergory: notes
<obj>.%Get(0).documentAttachments.%Get(1).guid: 23433
<obj>.%Get(0).documentAttachments.%Get(1).id: 153
<obj>.%Get(0).documentAttachments.%Get(1).catergory: summary

USER>zsho obj:2
<obj>.%Get(0).patient: 11@%Library.DynamicObject
<obj>.%Get(0).visit: 2@%Library.DynamicArray
<obj>.%Get(0).documentAttachments: 21@%Library.DynamicArray

USER>zsho obj::"visit"
<obj>.%Get(0).visit.%Get(0).guid: 45678
<obj>.%Get(0).visit.%Get(0).date: 2020-01-10
<obj>.%Get(0).visit.%Get(0).reason: other
<obj>.%Get(0).visit.%Get(1).guid: 45679
<obj>.%Get(0).visit.%Get(1).date: 2020-01-11
<obj>.%Get(0).visit.%Get(1).reason: routine

USER>zsho obj::"guid"
<obj>.%Get(0).patient.guid: 12345
<obj>.%Get(0).visit.%Get(0).guid: 45678
<obj>.%Get(0).visit.%Get(1).guid: 45679
<obj>.%Get(0).documentAttachments.%Get(0).guid: 23432
<obj>.%Get(0).documentAttachments.%Get(1).guid: 23433 

Have a nice day...

It's simple and easy... just follow the picture (I meant, the JSON string):

[
  {
    "patient":
      {
        "guid":"12345",
        "id":12345,
        "forename":"Joe",
        "surname":"Bloggs",
        "dateOfBirth":"2002-12-10T00:00:00Z",
        "NOK":[
                {
                   "NOKname":"Alison Bloggs",
                   "NOKrelationship":"Wife",
                   "telephone":"02081234567",
                   "email":"alison@bloggs.com"
                }
              ]
      }
  }
]

Now we create a dynamic Object:

set obj = {}.%FromJSON(pData)

Your pData is a JSON-Array, where the (array)elements/items are objects

 (we have just one elemment), so we can say:

set item = obj.%Get(0)

This  item object has a patient property, which is an object, so we go one step deeper

set item = obj.%Get(0)
set patient = item.patient 
set patient = obj.%Get(0).patient  // same as above

Our patient object has properties like guid,  idforename, surname, dateOfBirth and NOK

The property NOK itself is, again, an array where the array elements/items are objects.

set nok = patient.NOK
set nok = obj.%Get(0).patient.NOK // same as above

Now, we know, our property NOK is an array, so we have to take of those elements. This nok elements are objects, so we can take the properties (NOKname, ...email):

set nokItem = nok.%Get(0)
write nokItem.NOKname," ",nokItem.email,!

or, all in one line

write obj.%Get(0).patient.NOK.%Get(0).NOKname,!

Now all together, a small routine to print out all NOKxxxx properties:

set obj = {}.%FromJSON(pData)
for i=0:1:obj.%Size()-1 { // loop over pData items grabbing patient props
    set patient = obj.%Get(i).patient
    for j=0:1:patient.NOK.%Size()-1 { // now we loop over all NOK items
        set nok = patient.NOK.%Get(j)
        write nok.NOKname,!
        write nok.NOKrelationship,!
    }
}

So I hope, you see the light at the end of the tunnel...

According to above sample data, your JSON-Data (pData) is :

- an array of one or more objects

- the object(s) have "patient", "visit" and "documentAttachments" as properties

- the "patient" property is an object and has "guid", "id", "forename", "surname", and "dateOfBirth" as properties

So, to get the "forename" property, you have to do something like:

set object = {}.%FromJSON(pData)  // create a JSON-Object from JSON string
write object.%Get(0).patient.forename  // 

or step by step

set firstArrayItem = object.%Get(0) // first array item is 0
set patient = firstArrayItem.patient // this is the patioent object
write patient.forename  // now you have the name
write patient.surname // and other properties

By the way, the "visit" property is also an array! To list all the "reason" properties, you have to write a loop:

set visit = firstArrayItem.visit  // the first visit 
for i=0:1:visit.%Size()-1 {
  write visit.%Get(i).reason,!
}

Your output will be:

other

routine

I hope, things are now more clear? 

The problem is obvious, your set object ##class(%DynamicAbstractObject).%FromJSON(pData) won't work, because pData contains an invalid JSON!

1) "visit":[{"guid":45678" <-- surplus QUOTE-char, maybe a typo

2) reason":"other"} {"guid":45679    missing comma between } and {

3) "catergory":"notes"} {"guid":"23433" missing comma between } and {

You  can use https://jsonlint.com/ to check your JSON string.

The above should work, so the question is, what is the content of pData. Do you can share a sample with us?

Also, you can write the above somewhat shorter too and you can access all the properties either via propertyname or via the %Get() method.

I assume, you have a string like this:

set pData="{""Name"":""Joe"", ""Age"":50}"

the you can do this

set object={}.%FromJSON(pData)
write object.Name // Joe
write object.%Get("Name") // Joe
write pData // {"Name":"Joe", "Age":50}

Now we have the year 2020 (but I will discard this year from my life because of Covid-19) but our loved COS wasn't in this year nor in  the very past years invented.

COS (better, the predecessor, M ) has more then 50 years under his belt, it was created in the late sixties - in a time, where RAM was rare, expensive and therefore it was measured in units of kilobyte!

If I recall correctly, at end of the seventies, we had some customers on a PDP-11/34 with less then 64KB of memory, one or two 2kb partitions and some "big" partitions with 4KB or 6KB, the rest was for disk buffers (512 byte each) and DEC's V4b (which acted as OS, database and language interpreter).

Hence the ability of COS for excessive abbreviations and if you get an old routine, written by an old school programmer (like me) then you will see there (mostly) abbreviated commands,  postconditions and (local-/globalvariable, label and routine) names, consisting just a few letters (one to three, maybe four).

Unfortunately, this (abbreviated commands, short names) happens to me in this days too - you know, old habits never die.

There is a wonderful (and at the same time a horrific) example of such a routine: one letter names, everything abbreviated:
https://stackoverflow.com/questions/4151554/need-mumps-sample-code/4430996

clock ;; a digital clock from
;; https://stackoverflow.com/questions/4151554/need-mumps-sample-code/4430996
;;
Q N R,Q,C,D,E,W,B,G,H,S,T,U,V,F,L,P,N,J,A S N=$G(N),Q='N,F=Q+Q,P=F+F,W=$L($T(Q))
 S W=$E(W,Q),S='N_+N,W=W-F*S,L=$G(L),R=$C(Q_F_P),R(F)=$C(F+Q_F),R(P)=$C(W-F) W #
 S T=$E($T(Q+F),F,W\S)_$C(W+S+F) X T S B=$P(T,$C(P_P),F),C=B\(W*W),D=B-(C*W*W)\W
 F G=S-Q:F:S+F+Q S E=B-(C*W*W+(D*W)),H=$E($T(Q),G),@H=$S(@H<S:'Q,Q:N)_@H,T=C_D_E
 F A=Q:Q:W\S S J=$E(T,A),C(F)=$S(J>(F+Q)&(J<(S-F)):Q,Q:+N),C(P)=$S(J#F:Q,Q:+N) D
 .S C(Q)=$S(J<(S-F):+N,Q:Q),C(F+Q)=$S(J>Q&(J<(S-F))&(J'=(P+'L))&(J'=(P)):Q,Q:+N)
 .S H('L)=L F  S H(N?.E)=$O(C(H('$G(N)))) Q:H('+L)=L  S F(A,H('L))=C(H(W[(W\S)))
 F U=Q:Q:P W !,R F V=Q:Q:P+F W $S(F(V,U):'Q,Q:$C(P_(W\S))) W:'(V#F) $C('N_F_F+F)
 W !!,R(F)_C_R(P)_D_R(P)_E_R(F) X $RE($E($T(Q),Q+F,P+Q))_R(P)_'N W # G:N=L Q+F Q

It's not only tricky to read but has an attractive form too