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...

Julius Kavay · Oct 31, 2025 go to post

JavaScript uses underscores as separators in (big)number literals.

<script>alert(1_234);</script> displays 1234
<script>alert(12_345_678_901_234_567_890_123n);</script>  displays 12345678901234567890123

Maybe there is a quirk/problem in VS terminal...

Julius Kavay · Sep 28, 2025 go to post

Oh yes, you are able to create an instance of

Class Your.Page Extends %CSP.Page
{
Property Name As %String [ InitialExpression = "Joe" ];
}

class because the inheritance goes as follows:
Your.Page <-- %CSP.Page <-- %Library.Base <-- %Library.SystemBase
and the %Library.SystemBase donates you the %New() method.

Julius Kavay · Sep 28, 2025 go to post

My guess is, because abstract classes (%CSP.Page is an abstract class) can't be instantiated, your subclass lacks the generator methos for property initialisation.

But there is a simple solution:

Class Your.Page Extends (%RegisteredObject, %CSP.Page)
{
Property Name As %String [ InitialExpression = "Joe" ];
}

Well, the world is right again

set page=##class(Your.Page).%New()
write page.Name --> Joe
Julius Kavay · Aug 18, 2025 go to post

Ha ha ha 😂, that's a big mistake. Those are old functions (for even older applications, maintained for backward compatibility only)  in the mean time all replaced by the $list...() functions.

Julius Kavay · Aug 18, 2025 go to post

Try it this way...

Class DC.OldStuff Extends %Persistent [ StorageStrategy = NewStorage1 ]
{

Property Rec As %Integer [ Identity ];
Property Name As %String;
Property City As %String;
Property Phone As %String;
Storage NewStorage1
{
<SQLMap name="Map1">
<Data name="City">
<RetrievalCode>s {*}=$zel(^myGlo("R",{L2}),2)</RetrievalCode>
</Data>
<Data name="Name">
<RetrievalCode>s {*}=$zel(^myGlo("R",{L2}),1)</RetrievalCode>
</Data>
<Data name="Phone">
<RetrievalCode>s {*}=$zel(^myGlo("R",{L2}),3)</RetrievalCode>
</Data>
<Global>^myGlo</Global>
<Subscript name="1">
<Expression>"R"</Expression>
</Subscript>
<Subscript name="2">
<Expression>{Rec}</Expression>
</Subscript>
<Type>data</Type>
</SQLMap>
<StreamLocation>^DC.OldS</StreamLocation>
<Type>%CacheSQLStorage</Type>
}

}

A short test shows, it works

USER>k ^myGlo
USER>s ^myGlo("R",1)=$zlp("John,Boston,11-22-33")
USER>s ^myGlo("R",5)=$zlp("Laura,New York,333-444-555")
USER>s ^myGlo("R",7)=$zlp("Paul,Chicago,556-666-777")
USER>d $system.SQL.Shell()
SQL Command Line Shell
----------------------------------------------------

The command prefix is currently set to: <<nothing>>.
Enter q to quit, ? for help.
USER>>select * from DC.OldStuff
3.      select * from DC.OldStuff

ID      City    Name    Phone   Rec
1       Boston  John    11-22-33        1
5       New York        Laura   333-444-555     5
7       Chicago Paul    556-666-777     7
3 Rows(s) Affected
statement prepare time(s)/globals/lines/disk: 0.0003s/5/159/0ms
          execute time(s)/globals/lines/disk: 0.0003s/13/1136/0ms
                          cached query class: %sqlcq.USER.cls43
---------------------------------------------------------------------------
USER>>quit

or as objects

USER>s obj=##class(DC.OldStuff).%OpenId(7)

USER>w obj.Name,!,obj.City,!,obj.Phone
Paul
Chicago
556-666-777
USER>
Julius Kavay · Aug 14, 2025 go to post

First step: create your own method, for example

Class DC.Unix [ Abstract ]
{
/// Convert Posix time into Timestamp
/// 
/// posix: posix time
///    df: date format
///    tf: time format
///    dp: decimal places
ClassMethod PosixToTimeStamp(posix, df = 3, tf = 1, dp = 0)
{
	set posix=posix-1152921504606846976/1E6
	quit $zdt(posix\86400+47117_","_(posix#86400),df,tf,dp)
}
}

Next step, if you need more speed, instead of the parameters <df> and <tf> use constants and remove <dp>. The very last step: shorten the method name from PosixToTimeStamp() to P2TS()

This way I got the (nearly) the same times as with (%Library.PosixTime).LogicalToTimeStamp, but without the need for string manipulation 

ICINDY:USER>k

ICINDY:USER>set posix = 1154669852181849976

ICINDY:USER>while $zh#1 {} s t=$zh f i=1:1:1E6 { s x=##class(%Library.PosixTime).LogicalToTimeStamp(posix) } w $zh-t
.902538
ICINDY:USER>while $zh#1 {} s t=$zh f i=1:1:1E6 { s x=##class(%Library.PosixTime).LogicalToTimeStamp(posix) } w $zh-t
.90609
ICINDY:USER>

ICINDY:USER>while $zh#1 {} s t=$zh f i=1:1:1E6 { s x=##class(DC.Unix).PosixToTimeStamp(posix) } w $zh-t
.934834
ICINDY:USER>while $zh#1 {} s t=$zh f i=1:1:1E6 { s x=##class(DC.Unix).PosixToTimeStamp(posix) } w $zh-t
.944418
ICINDY:USER>

ICINDY:USER>while $zh#1 {} s t=$zh f i=1:1:1E6 { s x=##class(DC.Unix).P2TS(posix) } w $zh-t
.913609
ICINDY:USER>while $zh#1 {} s t=$zh f i=1:1:1E6 { s x=##class(DC.Unix).P2TS(posix) } w $zh-t
.905303
ICINDY:USER>

ICINDY:USER>w $zv
IRIS for UNIX (Ubuntu Server LTS for x86-64) 2021.2 (Build 649U) Thu Jan 20 2022 08:49:51 EST
ICINDY:USER>w x
2025-05-27 12:06:15
ICINDY:USER>
Julius Kavay · Jul 20, 2025 go to post

You have a status (or error code), OK, now take that code and make it more readable:

set code=<put here your code>
write $system.Status.GetErrorText(code)

I think, it will be some kind of authentication issue...

Julius Kavay · Jul 7, 2025 go to post

In SystemManagementPortal goto:

SystemAdministration --> Configuration --> AdditionalSettings --> StartupSettings: JobServers

Julius Kavay · Jul 7, 2025 go to post

I think, Enrico was more aiming for the point that “concatenation is the gateway for SQL injection.”

Julius Kavay · Jul 3, 2025 go to post

An easy and simple (assuming standard storage strategy) way to get those informations (the slot numbers, where a property is stored) could be achieved with a simple classmethod

Class DC.PropInfo [ Abstract ]
{

/// Info about properties of a class:
/// - list of all properties stored in a list
/// - slot number for a given property
/// 
/// 1) add this class to your class definition
///    class some.class extends (%Persistent, DC.StorageInfo)  or
///    class some.class extends (%SerialClass, DC.StorageInfo)
///           
/// 2) then use it as follows
///    write ##class(some.class).PropInfo()      --> list of property names
///    write ##class(some.class).PropInfo("Age") --> slot number for the Age property
/// 
/// write ##class(
ClassMethod PropInfo(name = "") As %String [ CodeMode = objectgenerator ]
{
	set sto=%compiledclass.Storages, prp=0, list=""
	
	if sto.Count()=1 {
		set dat=sto.GetAt(1).Data
		for i=1:1:dat.Count() if dat.GetAt(i).Structure="listnode" set prp=dat.GetAt(i) quit
	
		if prp {
			if %compiledclass.ClassType="serial" { set list="", i=1 } else { set list=$lb(""), i=2 }
			for i=i:1:prp.Values.Count() set list=list_$lb(prp.Values.GetAt(i).Value)
		}
		
		do %code.WriteLine(" if name="""" quit """_$lts(list)_"""")
		do %code.WriteLine(" quit $lf($lfs("""_$lts(list)_"""),name)")
	}
	
	if list="" write !,"*** No properties found! ***"
	quit $$$OK
}

}

Two test classes

Class DC.TestPerson Extends (%Persistent, DC.PropInfo)
{
Property Name As %String;
Property Addr As DC.Address;
Property Salary As %Numeric;
Property Expertise As list Of %String;
}


Class DC.Address Extends (%SerialObject,DC.PropInfo)
{
Property Street As %String;
Property City As %String;
}

Now you can do things like:

write ##class(DC.TestPerson).PropInfo()         --> ,Name,Addr,Salary,Expertise
write ##class(DC.TestPerson).PropInfo("Salary") --> 4
// you can use the slot number for direct data access:
write $list(^DC.TestPersonD(id),slotnumber)  gives you the same value as
write ##class(DC.TestPerson).SalaryGetStored(id)

// the same game for serial classes
write ##class(DC.Address).PropInfo()       --> Street,City
write ##class(DC.Address).PropInfo("City") --> 2

// in case you have an instance
set pers=##class(DC.TestPerson).%OpenId(id)
write pers.PropInfo()      --> ,Name,Addr,Salary,Expertise
write pers.Addr.PropInfo() --> Street,City

// etc.

Julius Kavay · Jun 30, 2025 go to post

Another reason for the <CLASS or ROUTINE DOES NOT EXISTS> message is that you're in the wrong namespace. Your installation has four namespaces by default: %SYS, DOCBOOK, SAMPLES, and USER. Another possibility could be misleading YouTube videos that use classes from newer Cache or IRIS versions without notice.