The jsonProvider code may also be invoked from a non-Zen context by calling one of the following APIs:

  • %WriteJSONFromArray
  • %WriteJSONFromObject
  • %WriteJSONStreamFromArray
  • %WriteJSONStreamFromObject
  • %ConvertJSONToObject

proof

Example:

##class(%ZEN.Auxiliary.jsonProvider).%WriteJSONStreamFromObject(.stream,..%OpenId(1),,,1,"aelqoc")
"<- json = ",stream.Read(),!

If you know how to work with ActiveX from MS Excel VBA, then there is no problem.

E.g. (demo.vbs):

Set f = CreateObject("CacheActiveX.Factory")
Set rs = CreateObject("CacheActiveX.ResultSet")
If Not f.IsConnected() Then

  f.Connect("cn_iptcp:127.0.0.1[1972]:SAMPLES:_SYSTEM:SYS")

  Set rs=f.DynamicSQL("select TOP 3 * from Sample.Person")
  rs.Execute()
  while rs.Next
    WScript.Echo rs.Get("SSN"'print of field SSN for first three rows from the table Sample.Person
  wend

  rs.Close()
  Set person = f.Static("Sample.Person")
  age=person.CurrentAge(45678) 'call of method of class Sample.Person

  WScript.Echo age
End If

Running a Query in Visual Basic

If you only need SQL access, then will be easier to create a view (CREATE VIEW), if need both, then - %CacheSQLStorage, e.g.:

Class demo.A Extends %Persistent
{

Property P1;

Property P2;

ClassMethod Fill()
{
  ..%KillExtent()
  
  i=1:1:3 {
    t=..%New()
    t.P1="P1_"_i
    t.P2="P2_"_i
    t.%Save()
  }
}

Storage Default
{
<Data name="ADefaultData">
  <Value name="1">
    <Value>%%CLASSNAME</Value>
  </Value>
  <Value name="2">
    <Value>P1</Value>
  </Value>
  <Value name="3">
    <Value>P2</Value>
  </Value>
</Data>
<DataLocation>^demo.AD</DataLocation>
<DefaultData>ADefaultData</DefaultData>
<IdLocation>^demo.AD</IdLocation>
<IndexLocation>^demo.AI</IndexLocation>
<StreamLocation>^demo.AS</StreamLocation>
<Type>%Library.CacheStorage</Type>
}

}

Class demo.B Extends %Persistent FinalStorageStrategy = Default ]
{

Parameter READONLY = 1;

Property P2;

Storage Default
{
<SQLMap name="BDefaultData">
  <Data name="P2">
    <Piece>3</Piece>
  </Data>
  <Global>^demo.AD</Global>
  <RowIdSpec name="1">
    <Expression>{L1}</Expression>
    <Field>ID</Field>
  </RowIdSpec>
  <Subscript name="1">
    <Expression>{ID}</Expression>
  </Subscript>
  <Type>data</Type>
</SQLMap>
<StreamLocation>^demo.AS</StreamLocation>
<Type>%CacheSQLStorage</Type>
}

}

Result:

USER>##class(demo.A).Fill()
 
USER>d $SYSTEM.SQL.Shell()
SQL Command Line Shell
----------------------------------------------------
 
The command prefix is currently set to: >.
Enter q to quit, ? for help.
USER>>select * from demo.A
1.      select * from demo.A
 
ID      P1      P2
1       P1_1    P2_1
2       P1_2    P2_2
3       P1_3    P2_3
 
3 Rows(s) Affected
statement prepare time(s)/globals/lines/disk: 0.1426s/46110/260143/45ms
          execute time(s)/globals/lines/disk: 0.0004s/16/809/0ms
                          cached query class: %sqlcq.USER.cls12
---------------------------------------------------------------------------
USER>>select * from demo.B
2.      select * from demo.B
 
ID      P2
1       P2_1
2       P2_2
3       P2_3
 
3 Rows(s) Affected
statement prepare time(s)/globals/lines/disk: 0.0696s/44550/243602/0ms
          execute time(s)/globals/lines/disk: 0.0002s/4/619/0ms
                          cached query class: %sqlcq.USER.cls13
---------------------------------------------------------------------------
USER>>quit
 
USER>##class(demo.B).%OpenId(3).P2
P2_3

Ok, exclusively for fun.

I made some improvements and now my score is 9, but if you try very hard, even - 0!
Who less ? ;)

Here is the code:

Class ITPlanet.Task2 Abstract ]
{

Parameter p = {$zwbunpack("㤸㜶㔴㌲㄰")};

ClassMethod main() As %String
{
 ..#p
}

}

Class ITPlanet.Test Abstract ]
{

ClassMethod length(
  class = {$classname()},
  method "main"As %Integer CodeMode = expression ]
{
##class(%Dictionary.MethodDefinition).IDKEYOpen(classmethod).Implementation.Size
}

ClassMethod test(makeDeploy = {$$$NO})
{
  ;do ##class(ITPlanet.Test).test()

  set classname="ITPlanet.Task2"
  set check=9876543210
  do:makeDeploy $system.OBJ.MakeClassDeployed(classname)
  set result=$classmethod(classname,"main")
  write !,result,!,check,
        !,"correct: ",$select(result=check:"yes",1:"no"),
        !,"length: ",..length(classname)
}

}
USER>do ##class(ITPlanet.Test).test()
 
9876543210
9876543210
correct: yes
length: 9
USER>do ##class(ITPlanet.Test).test(1)
 
9876543210
9876543210
correct: yes
length: 0

You can use the class %ZEN.proxyObject, e.g.:

Class demo.Customer Extends %RegisteredObject
{

Property p1 As %String;

Property p2 As %String;

Property list As %Collection.ListOfDT;

/// d ##class(demo.Customer).Test()
ClassMethod Test()
{
  customer=##class(demo.Customer).%New()
  customer.p1="p1"
  customer.p2="p2"
  customer.list=##class(%ListOfDataTypes).%New()
  customer.list.InsertList($lb(1,"two",,"four"))
  
  appointment=##class(%ZEN.proxyObject).%New()
  appointment.a1="a1"
  appointment.a2="a2"
  appointment.list=##class(%ListOfDataTypes).%New()
  appointment.list.InsertList($lb(2,3,"test",8))
  
  ; Cancellation
  response=##class(%ZEN.proxyObject).%New()
  response.Code="Cancellation"
  response.Info="Info_Cancellation"
  response.Code,":",! response.%ToJSON(,"2aelow")
  
  response.%Clear()

  ; Customer
  response.Code="Customer"
  response.Info=customer
  
  !!,response.Code,":",! response.%ToJSON(,"2aelow")

  response.%Clear()

  ; Appointment
  response.Code="Appointment"
  response.Info=appointment
  
  !!,response.Code,":",! response.%ToJSON(,"2aelow")
}

}
USER>##class(demo.Customer).Test()
Cancellation:
{
  "Code":"Cancellation",
  "Info":"Info_Cancellation"
}
 
Customer:
{
  "Code":"Customer",
  "Info"{
    "p1":"p1",
    "p2":"p2",
    "list":["1","two","","four"]
  }
}
 
Appointment:
{
  "Code":"Appointment",
  "Info"{
    "a1":"a1",
    "a2":"a2",
    "list":[2,3,"test",8
    ]
  }
}

Refine your version $zv. Perhaps in older versions there were bugs that were then fixed.

Simple test:

Class my.children Extends %Persistent
{

Property p1;

Relationship parent As my.parent [ Cardinality = parent, Inverse = children ];

Method %OnConstructClone(
  object As %RegisteredObject,
  deep As %Boolean = 0,
  ByRef cloned As %String) As %Status [ Private, ServerOnly = 1 ]
{
  i%p1 = object.p1 _ " cloned"
  $$$OK
}
}

Class my.parent Extends %Persistent
{

Property p1;

Relationship children As my.children [ Cardinality = children, Inverse = parent ];

ClassMethod Fill()
{
  ;d ##class(my.parent).Fill()
  
  ..%KillExtent()
  
  i = 1:1:3 {
    = ..%New()
    p.p1 = "parent["_i_"]"
    j = 1:1:2 {
      = ##class(my.children).%New()
      c.p1 = $$$FormatText("children[%1,%2]",i,j)
      p.children.Insert(c)
    }
    p.%Save()
  }
  zw ^my.parentD
  !,"_______",!!
  
  i = 1,3 ..%OpenId(i).%ConstructClone($$$YES).%Save()

  ; or so - the result is the same
  ; f i = 1,3 d ..%OpenId(i).%ConstructClone($$$NO).%Save()

  zw ^my.parentD
}

Method %OnConstructClone(
  object As %RegisteredObject,
  deep As %Boolean = 0,
  ByRef cloned As %String) As %Status [ Private, ServerOnly = 1 ]
{
  i%p1 = object.p1 _ " cloned"
  $$$OK
}
}


USER>$zv
Cache for Windows (x86-64) 2017.2 (Build 672U) Wed May 10 2017 20:43:42 EDT
USER>##class(my.parent).Fill()
^my.parentD=3
^my.parentD(1)=$lb("","parent[1]")
^my.parentD(1,"children",1)=$lb("","children[1,1]")
^my.parentD(1,"children",2)=$lb("","children[1,2]")
^my.parentD(2)=$lb("","parent[2]")
^my.parentD(2,"children",3)=$lb("","children[2,2]")
^my.parentD(2,"children",4)=$lb("","children[2,1]")
^my.parentD(3)=$lb("","parent[3]")
^my.parentD(3,"children",5)=$lb("","children[3,2]")
^my.parentD(3,"children",6)=$lb("","children[3,1]")
 
_______
 
^my.parentD=5
^my.parentD(1)=$lb("","parent[1]")
^my.parentD(1,"children",1)=$lb("","children[1,1]")
^my.parentD(1,"children",2)=$lb("","children[1,2]")
^my.parentD(2)=$lb("","parent[2]")
^my.parentD(2,"children",3)=$lb("","children[2,2]")
^my.parentD(2,"children",4)=$lb("","children[2,1]")
^my.parentD(3)=$lb("","parent[3]")
^my.parentD(3,"children",5)=$lb("","children[3,2]")
^my.parentD(3,"children",6)=$lb("","children[3,1]")
^my.parentD(4)=$lb("","parent[1] cloned")
^my.parentD(4,"children",7)=$lb("","children[1,2] cloned")
^my.parentD(4,"children",8)=$lb("","children[1,1] cloned")
^my.parentD(5)=$lb("","parent[3] cloned")
^my.parentD(5,"children",9)=$lb("","children[3,2] cloned")
^my.parentD(5,"children",10)=$lb("","children[3,1] cloned")

> Can I make a serial class always computed?
Yes, of course.

Class CS.Serial Extends %SerialObject [ NoExtent ]
{
Property Year As %Integer;
Property Month As %Integer;
}

Class CS.Persistent Extends %Persistent
{

Property data As CS.Serial [ SqlComputeCode = {set {*} = ##class(CS.Persistent).dataGetStatic()}, SqlComputed, Transient ];

ClassMethod dataGetStatic() As %List
{
  quit $lb(2017,1)
}
}

Output:
^CS.PersistentD=1
^CS.PersistentD(1)=$lb("")
ID      data
1       $lb(2017,1)
 
1 Rows(s) Affected

It all depends on what you wish to achieve.
For example:

Class CS.Serial Extends %SerialObject
{
Property Year As %Integer;
Property Month As %Integer;

Method %MyGetSerial() As %List [ ServerOnly = 1 ]
{
  q $lb(i%Year,i%Month)
}
}

Class CS.Persistent Extends %Persistent
{
/// Property is triggered computed
Property data1 As CS.Serial [ SqlComputeCode = {set {*} = ..dataGetStatic()}, SqlComputed ];
/// Property is always computed
Property data2 As %List [ SqlComputeCode = {set {*} = ##class(CS.Persistent).dataGetStatic()}, SqlComputed, Transient ];

/// data getter method in SQL context
ClassMethod dataGetStatic() As %List
{
  s s=##class(CS.Serial).%New(), s.Month=1, s.Year=2017
  q s.%MyGetSerial()
  ; or
  q $lb(2017,1)
}
}

---------

select ID,data1,data2,data1_Month,data1_Year from CS.Persistent

ID  data1   data2   data1_Month  data1_Year
1   2017,1  2017,1  1            2017
2   2017,1  2017,1  1            2017
3   2017,1  2017,1  1            2017

Important: pre-need string to convert to UTF-8

USER>w ##class(%xsd.hexBinary).LogicalToXSD($system.Encryption.SHAHash(512,$zcvt("тестtest","O","UTF8")))
83B481B1A58FA416F34BFF575F2DCA866C6AB790CF0941BC0F1E7E91D916D22866CB9575C666021B2F8A796B20A484ABB29403853EDEA6173B9A61758060117E
USER>w ##class(%xsd.hexBinary).LogicalToXSD($system.Encryption.SHAHash(512,$zcvt("test","O","UTF8")))
EE26B0DD4AF7E749AA1A8EE3C10AE9923F618980772E473F8819A5D4940E0DB27AC185F8A0E1D5F84F88BC887FD67B143732C304CC5FA9AD8E6F57F50028A8FF