Sometimes, like now, it's a good idea to give a variable an initial value...

set %out="", key="" for {...

One more comment, I'm not sure about the "quit" command after the closing curly brace,  I think you don't need it (because it's outside of the FOR loop, hence it will stop the flow of your application)!

I would say, a little bit to much "set" command.

Instead   of: Set tQStr = $$$ArrayToString(pQArray,tQStr,"##") 

try this  one: $$$ArrayToString(pQArray,tQStr,"##") 

Your version gives something like: Set tQStr = set %out="" for { ...

as you see, %out isn't a command (and set would be undefined, but that isn't the problem)

By the way, the second line of your macro should be:

set key=$order(%array(key)) quit:key="" ##continue

else you have the perfect endless loop

Why do you need an AI for those questions?

4) and 5) I'm pretty sure, both questions were already answered by the creators of MUMPS back in the 60es, without AI

ClassMethod MAX(a,b,c,d)
{
  quit $$max($$max(a,b),$$max(c,d))

max(x,y) quit $s(x>y:x,1:y)
}

/// Compare alphanumeric data
/// OUT: a<b --> -1
///      a=b -->  0
///      a>b -->  1
ClassMethod AlphaCompare(a,b)
{
  quit $s(a=b:0, a]b:1, 1:-1)
}

6) CPU for this process - What do you mean by that?
  a) on a multicore CPU, you ask for the core number for your process?
     If yes, I think, you won't get that number, because
     usually, a process runs on the same core, the emphasis is on "usually" but the OS may shift a process, according to load, to another  core.
     If you could have a code like:
     set currentCore=$$GetTheCurrentCore($j)
     write "Current core: ",currentCore,!
     in the next point of time that information could be wrong
     
  b) do $system.CPU.Dump()
     gives you an info about the CPU, your proces runs on
  
  c) write ##class(%SYS.ProcessQuery).GetCPUTime()
     gives you two CPU-times, system- and user-time in milliseconds
 

Tipp #10 gives us plenty of opportunity for discussions like 
a) may that %DynArray have an embedded %DynArrays [1, 2, ["a", "b"], 4, 5]
b) may an element contain a comma                                [1, 2, "a,b", 4]
etc.

instead of the original solution: Set str = $TRANSLATE(jsonArray.%ToJSON(), "[]") 
I would use a more accurate way : set str = $e(jsonArray.%ToJSON(),2,*-1)

That's neither a trick nor a feature but an intended behavoir (for what feels like 100 years).
Each unique index over one or more table columns can be used to open that object just by using the column(s) from that index.

class DC.Example2 Extends %Persistent { 
Property Column1 As %String; 
Property Column2 As %String; 
Property Column3 As %String; 
Property Column4 As %String; 
    
Index one On Column1 [Unique]; 
Index two On (Column2,Column3) [Unique]; 
} 

set obj=##class(DC.Example2).oneOpen(colValue1)   // default lock, or 
set obj=##class(DC.Example2).oneOpen(colValue1,0) // without lock 
if (obj) { write obj.%Id() } else { write "Record does not exist or the index one is corrupted" } 

set obj=##class(DC.Example2).twoOpen(colValue2,colValue3)   // default lock, or 
set obj=##class(DC.Example2).twoOpen(colValue2,colValue3,0) // without lock 
if (obj) { write obj.%Id() } else { write "Record does not exist or the index two is corrupted" } 

Some time ago I noticed (rather by chance) the presence of some other (new) types, but I did not look more closely into the what and why of those codes - I'm just happay with the "old ones", that I use as menitioned above.
Thank you for the explanation.

I know that the $lb() representation of a null string is $c(2,1) on all instances (8-bit or Unicode), but passing the argument $c(2,2) to $list() is still correctly interpreted (respectively extracted) as a null string by the function.

set result = $c(3,4,125,2,2,5,1,97,98,99)    // return value from a C-program
set mylist = $lb(125,"","abc")

zwrite result  --> result=$lb(125,"","abc")  // correct 
write $listsame(result,mylist) --> 0         // because the type bytes are different

but after executing the following

if $list(result,2) = $list(mylist,2) { write "YES" } else { write "NO" }
if $list(result,2) = "" { write "YES" } else { write "NO" }

you will see two YESes which proofs my answer from 18. Apr.

Just two comments:
"Observations: ... payload grows only when required" is correct but a more correct explanation would be "the whole list structure is created with a minimum memory usage in mind".

The above note (minimum storage size) leads to special cases:

1) an integer 0 is stored in two bytes only
   02 04     (length, type) and not
   03 04 00  (length, type, data) which is also accepted(*)
   
   The same goes for nullstring (which is obvious)
   02 01     (length, type and, of course, no data), ASCII nullstring
   02 02     (length, type) a "Unicode" nullstring, 
   
2) If only the length component is present and equals 1,
   then this indicates a NULL element (i.e. a missing element):

set x=$lb(85,,,0,"","abc")
zzdump x --> 03 04 55 01 01 02 04 02 01 05 01 61 62 63

which breaks down into
03 04 55         $li(x,1) = 85
01               $li(x,2) = <NULL VALUE>
01               $li(x,3) = <NULL VALUE>
02 04            $li(x,4) = 0
02 01            $li(x,5) = "" / nullstring
05 01 61 62 63   $li(x,6) = "abc"

(*) I use this side effect (list use minimal bytes for integers) and ASCII strings are accepted even if their type is unicode) in some of my CallOuts to return results (as an IRIS-List) without explicitly converting C's two byte string into one byte ASCII where it aplies and I return integer values either as four or eith bytes even if the value would fit in two, three or five bytes.

For example, the ReadColumn() method of the excel library class could return something like

set colData = %exl.ReadColumn(3)

zzdump colData
08 02 61 00 62 00 63 00 06 04 55 00 00 00   (ASCII "as" Unicode, 1 byte integer in 4 bytes)

zwrite colData
colData=$lb("abc",85)


Thank you guys at ISC for this wise implementation!
 

In the history of mankind there were three great inventions
- to light a fire
- the wheel
- and of course, the ^globals

There is nothing more to say.
 

The problem is NOT the QUIT in an IF statement, the problem is, using a QUIT WITH ARGUMENT in a TRY/CATCH statement.

First, there is no NaN in JSON.

set dynObj = {"val":($double("NAN"))}

write dynObj.val + 123 ---> "NAN

zwrite dynObj

dynObj={"val":($double("NAN"))}  ; <DYNAMIC OBJECT>

/* Question @devcommunity manager: how to exit the "insert code block" function? */

Second, red this for more understanding

If you take the JSON-part

{ "p1":"abcd", "p2":true, "p3":(value), "p4":(sum+2) }

and insert it into JSONlint then you get a nice red error message


Invalid JSON

Unexpected token '(', ..."rue, "p3":(value), ""... is not valid JSON(Line 1)


 

I'm not sure, do I understand you correctly... 
If you had a class and copied that class to a new class with some more properties, something like this

Class DC.OldData Extends %Persistent
{
Property FirstName As %String;
Property LastName As %String;

/// Redirect the %Open(), %OpenId() to the new class
///
ClassMethod %OnDetermineClass(oid As %ObjectIdentity, ByRef class As %String) As %Status [ ServerOnly = 1 ]
{
	Set class="DC.NewData"
	Quit $$$OK
}

/// Prevent creating a new instance of the old class
Method %OnNew() As %Status [ Private, ServerOnly = 1 ]
{
	Quit '$$$OK
}
}

Class DC.NewData Extends %Persistent
{
Property FirstName As %String;
Property LastName As %String;
Property MartialStatus As %String;
}

Maybe you send your new companion back to the school or maybe better, you look for a real companion with brain.
A docu for return is here By the way, did you know, AI is the shortcut for... no, no, it means Absent Intelligence!

Assuming, your XML stream never has more  then MAXSTRING characters AND if you can live with a "quick-and-dirty" solution then try this one

Class DC.XML.abc Extends (%RegisteredObject, %XML.Adaptor)
{
Property def As %String;
Property ghi As %String;
Property jkl As jkl;
ClassMethod Test(str)
{
    if $isobject(str) { set:str.Rewind() xml=str.Read(str.Size) } else { set xml=str }
    for{set i=$f(xml,"<![CDATA[",$g(i)) q:'i
        set j=$f(xml,"]]>",i) zt:'j "XMLE"
        set $e(xml,j-3,j-1)="", $e(xml,i-9,i-1)="", i=i-9
    }
    
    set rdr=##class(%XML.Reader).%New()
    if 'rdr.OpenString(xml) write "OpenErr",! quit
    do rdr.Correlate("abc","DC.XML.abc")
    while rdr.Next(.abc,.st) { zzdo abc }
}
}

Class DC.XML.jkl Extends (%RegisteredObject, %XML.Adaptor)
{
Property mno As mno;
}

Class DC.XML.mno Extends (%RegisteredObject, %XML.Adaptor)
{
Property pqr As %String;
Property stu As %String;
}

And some tests...

set s1="<abc><def>010203</def><ghi>040506</ghi><jkl><mno><pqr>070809</pqr><stu>101112</stu></mno></jkl></abc>"
set s2="<abc><def>010203</def><ghi>040506</ghi><jkl><![CDATA[<mno><pqr>070809</pqr><stu>101112</stu></mno>]]></jkl></abc>"
do ##class(DC.XML.abc).Test(s1)
def................................: 010203
ghi................................: 040506
jkl.mno.pqr........................: 070809
jkl.mno.stu........................: 101112
do ##class(DC.XML.abc).Test(s2)
def................................: 010203
ghi................................: 040506
jkl.mno.pqr........................: 070809
jkl.mno.stu........................: 101112

Note: the above ZZDO command takes an oref as argument and prints it, you can replace it with a simple zw oref.

I have made several callouts but never a callin (with userlogin), like you trying to do.
You wrote, you have several instances (Cache and IRIS) installed, presumably on the same hardware respective OS. Take a look in one of your Cache installation: <InstallDirectory>\dev\Cache\callin\
There are three files worth to study: sampcallin.c, sampcallint.c and shdir.c
The callin mechanism does not needs IPs and ports. To find out if anything has changed as a result of switching from Cache to IRIS, please contact WRC.

You do not need to reference 'localhost', your C++ program and IRIS are located (and running) on the same 'localhost', the source of your problem is something else

Nota Bene: I adopted Robert Cemper's suggestion ISOS, as shorthand for InterSystemsObjectScript

According to my opinion, your ISOS example (and the way, how JSON was implemented by ISC), is a mix of a little bit ISOS and a little bit JSON.

Why? Take this example:

set myObject = { "p1":"abcd", "p2":true, "p3":value, "p4":(sum+2) }
<- ObjScript -><-------------------- JSON------------------------->

The example starts with an ISOS syntax and proceeds with JSON syntax.
Unfortunately, the last two properties (p3, p4) are invalid JSON properties.
So we have a perfect mixture!

Remember, ISOS does not have literal constants (like true, false and null) and JSON neither has variables (like value) nor expressions, like (sum+2).

Since the current implementation has been in use for several years, a reimplementation is (probably) impossible. With other words, the bottom line is, unwillingly, but we have to accept the status quo...

Apparently, someone at ISC got a huge discount on parentheses (the easy solution) instead of requiring that these damned parentheses only be used when the assignment is an expression (and the user-friendly version would be: only when the expression contains the [ or ] operators!).
justmy2cents

 Put your variables into parentheses, i.e. 

 return {"value1":(val1), "value2":(val2)}
Julius Kavay · Dec 7, 2025 go to post

Could you please run the following commands in a terminal session:

write $zv
set exl=##class(%SYS.Python).Import("openpyxl")
set wbk=exl."load_workbook"( "your path_and_filename.xlsx" )
set sht=wbk.active
set cel=sht.cell(row,col)	// ROW and COL should point to a date-cell
write cel."data_type"
write cel.value.strftime("%d.%m.%Y")

Please post a screenshot here, even if you get some kind of error

Julius Kavay · Dec 7, 2025 go to post

Excel store date values as decimal values (or better, as flotaing poin) as dddd.tttt where ddd is the number of days since a base date (usually 1899-12-30) and tttt is: numberOfSecondsSinceMidnight / 86400, hence the value, you get is an date-object, and you have to format it according to your needs

Class DC.PyExcel Extends %RegisteredObject
{

ClassMethod Test(fn = "/home/kav/test/readtest.xlsx")
{
	set exl=##class(%SYS.Python).Import("openpyxl")
	set wbk=exl."load_workbook"(fn)
	set sht=wbk.active
	
	for row=1,2,3 {
		for col=1:1:3 {
			set cel=sht.cell(row,col)
			set typ=cel."data_type"
			set val=cel.value
			
			write ?col-1*15,$case(typ, "s":val, "n":$fn(val,",",2), "d":val.strftime("%a, %d.%m.%Y"), :"")
		}
		write !
	}
}

}

   

Julius Kavay · Nov 19, 2025 go to post

I'm just curious, what do you get, if you type write $view(-1,$job) in the output window of Studio?
See my screenshot, red=my input, yellow=Studio output

 

Julius Kavay · Nov 18, 2025 go to post

Your lines can be shortened to

if $system.Process.ClientExecutableName($j) = "..." { ... } else { ... }

justmy2cents

Julius Kavay · Nov 18, 2025 go to post

Port 1972 is the default port, your actual port may be different. That's why I added a comment to the answer above. Check the parameter file of your installation:

for IRIS : see iris.cpf, section [Startup], DefaultPort=...  
for Cache: see cache.cpf, section [Startup], DefaultPort=...

Julius Kavay · Nov 14, 2025 go to post

At least, there are four different ways to get that info

ClassMethod StudioTest()
{
	write "Call from Studio1: ",..InStudio1(),!
	write "Call from Studio2: ",..InStudio2(),!
}

/// Is the invocation from the Studio?
ClassMethod InStudio1()
{
	for i=$st:-1:1 if $st(i,"PLACE")["%Studio.General." ret 1
	ret 0
}

/// Is there a connection to Superserver?
ClassMethod InStudio2()
{
	set port=1972 // see iris.cpf, section [Startup], DefaultPort=...
	quit $p($v(-1,$j),"^",3)[("|TCP|"_port_"|")
}
Compiling routine DC.Util.1
Compilation finished successfully in 0.024s.

do ##class(DC.Util).StudioTest()
Call from Studio1: 1
Call from Studio2: 1


ICINDY:USER>; from PuTTY   -------------------

ICINDY:USER>do ##class(DC.Util).StudioTest()
Call from Studio1: 0
Call from Studio2: 0

 

The other two methods are: checking for the presence of
a) a dedicated variable or
b) a dedicated object
but both require a usage of undocumented functions

Julius Kavay · Nov 4, 2025 go to post

Well, I'm neither part of the ObjectScript nor the Objects developer team, hence I can't answer the "why" part of your question but fact is, timing mesuremenst show a significat higher speeds for the literal versions:
 

ClassMethod DynObject()
{
	while $zh#1 {} set t1=$zh for i=1:1:1E6 { if ##class(%DynamicArray).%New()  } set t1=$zh-t1
	while $zh#1 {} set t2=$zh for i=1:1:1E6 { if ##class(%DynamicObject).%New() } set t2=$zh-t2
	while $zh#1 {} set t3=$zh for i=1:1:1E6 { if [] } set t3=$zh-t3
	while $zh#1 {} set t4=$zh for i=1:1:1E6 { if {} } set t4=$zh-t4
	
	write "Times for :    Class  Literal     Diff",!
	write "DynArray  :", $j(t1,9,3), $j(t3,9,3), $j(t1/t3-1*100,9,2),"%",!
	write "DynObject :", $j(t2,9,3), $j(t4,9,3), $j(t2/t4-1*100,9,2),"%",!
}

The output will depend on
- IRIS/Cache version in use and
- on the underlying hardware
My values are

USER>d ##class(DC.Times).DynObject()
Times for :    Class  Literal     Diff
DynArray  :    0.665    0.401    65.90%
DynObject :    0.649    0.401    61.87%

Maybe someone else or the WRC has an explanation...