· Feb 27

%SerialObject property %Save error (%New method does not exist)


I wrote a class that extends %Persistent and has only a few properties, one of which is Serial, a %SerialObject property (it needs to store any class that extends %SerialObject). When I assign a value to this property and call %Save on the class instance, I get this:

<METHOD DOES NOT EXIST>zSerialNewObject+1^HS.Local.CZ.OR.ROUTER.Service.Data.KeyProperty.1 *%New,%Library.SerialObjectr
HSROUTERcJ$^zSerialNewObject+1^HS.Local.CZ.OR.ROUTER.Service.Data.KeyProperty.1 +1L$^zSerialGetSwizzled+2^HS.Local.CZ.OR.ROUTER.Service.Data.KeyProperty.1 +3G$^%AddToSaveSet+5^HS.Local.CZ.OR.ROUTER.Service.Data.KeyProperty.1 +16$^%BuildObjectGraph+6^%Library.RegisteredObject.1 +2G$^%Save+5^HS.Local.CZ.OR.ROUTER.Service.Request.RequestGenerator.1 +X^@ +1

I have not modified the compiled code manually. I have tried deleting the Storage definition for the class and recompiling. Did not work. I have tried creating an entirely new class, copying the properties in and compiling, that didn't work either.

What seems to be the problem here?

Thank you,

Jakub H

P.S. When I create a class with only one property, that being a %SerialObject, assign a subclass of it to that property and %Save, it works. So here are all the properties in the original class:

Class HS.Local.CZ.OR.ROUTER.Service.Data.KeyProperty Extends %Persistent

Property Key As %String(MAXLEN = 256);

Property IsObject As %Boolean [ InitialExpression = 0 ];

Property IsList As %Boolean [ InitialExpression = 0 ];

Property ObjectClass As %String(MAXLEN = 512);

Property ObjectId As %Integer;

Property Value As %String(MAXLEN = 3841144);

Property ValueList As list Of %String(MAXLEN = 3841144);

Property ObjectClassList As list Of %String(MAXLEN = 512);

Property ObjectIdList As list Of %Integer;

Property IsSerial As %Boolean [ InitialExpression = 0 ];

Property Serial As %SerialObject;

Property SerialList As list Of %SerialObject;

Product version: IRIS 2021.1
$ZV: IRIS for Windows (x86-64) 2021.1 (Build 215_0_21260U) Tue Nov 9 2021 19:30:33 EST
Discussion (4)3
Log in or sign up to continue

Can you provide small test case. It works fine for me. dc.demo is the same as HS.Local.CZ.OR.ROUTER.Service.Data.KeyProperty

dc.demoser is:

Class dc.demoser Extends %SerialObject

Property A As %String;


Test program:

set p = ##class(dc.demo).%New()
set x = ##class(dc.demoser).%New()
set x.A = "a1"
set p.Serial = x

set y= ##class(dc.demoser).%New()
set y.A = "a2"
do p.SerialList.Insert(y)

set ec= p.%Save()
zw ec

And then runnning:

USER>do ##class(dc.demo).test()

USER>zw ^dc.demoD

I never saw such direct of %SerialObject. Why do you need it? I wonder if %DynamicAbstractObject and JSON representation suits you here better

Hello Alexander,

thank you for your response!

I have ran a similar test to you and identified that the problem isn't with the single %SerialObject property, but with the one that is a list of them.

Here's the code:

ClassMethod test()
	set tKP = ..%New()
	set tAddressSerial = ##class(HS.Message.AddUpdateHubRequest).%OpenId(21986071).Addresses.GetAt(1)
	zw tAddressSerial
	set tKP.IsSerial = 1
	set tKP.Serial = tAddressSerial
	w tKP.%Save()

ClassMethod test2()
	set tKP = ..%New()
	set tIdentifierSerial = ##class(HS.Message.AddUpdateHubRequest).%OpenId(21986071).Identifiers
	zw tIdentifierSerial
	set tKP.IsSerial = 1
	set tKP.IsList = 1
	set tSerial = tIdentifierSerial.GetNext(.tKey)
	for {
		if tKey="" {quit}
		do tKP.SerialList.Insert(tSerial)
		set tSerial = tIdentifierSerial.GetNext(.tKey)
	w tKP.%Save()

The tXSerial variables are instances of classes that extend %SerialObject.

And here are the results:

HSROUTER>do ##class(HS.Local.CZ.OR.ROUTER.Service.Data.KeyProperty).test()
tAddressSerial=5@HS.Types.Address  ; <OREF>
+----------------- general information ---------------
|      oref value: 5
|      class name: HS.Types.Address
|           %%OID: $lb($lb("Fake Street","24","","52","","","","Townville","CZE","12345","","","HP","","A",""),"HS.Types.Address")
| reference count: 2
+----------------- attribute values ------------------
|               City = "Townville"
|            Country = ""
|             County = ""
|        Description = ""
|              POBox = ""
|         PostalCode = 12345
|           Precinct = ""
|        PrimaryFlag = ""
|              State = "CZE"
|             Status = "A"
|         StreetLine = "Fake Street"
|         StreetName = ""  <Get>
|       StreetNumber = "24"  <Get>
|         StreetType = 52  <Get>
|         UnitNumber = ""
|                Use = "HP"
HSROUTER>do ##class(HS.Local.CZ.OR.ROUTER.Service.Data.KeyProperty).test2()
tIdentifierSerial=2@%Collection.ListOfObj  ; <OREF>
+----------------- general information ---------------
|      oref value: 2
|      class name: %Collection.ListOfObj
| reference count: 3
+----------------- attribute values ------------------
|   ElementClassType = "serial"
|        ElementType = "HS.Types.Identifier"
|    LiteralBehavior = 0
|          (oidData) = ""
|       (oidData(1)) = $lb($lb("DS4","496228107","","PN","A",""))
|       (oidData(2)) = $lb($lb("111","496228107","","MB","A",""))
|         (orefData) = "2@%Collection.ListOfObj"
|        OrefStorage = 16393
|              Owner = 2
|           ReadOnly = ""
|            Storage = 16388
|     StreamLocation = ""
+--------------- calculated references ---------------
|               Size   <Get>
0 2Šx<METHOD DOES NOT EXIST>zSerialNewObject+1^HS.Local.CZ.OR.ROUTER.Service.Data.KeyProperty.1 *%New,%Library.SerialObject«
HSROUTERœJ$^zSerialNewObject+1^HS.Local.CZ.OR.ROUTER.Service.Data.KeyProperty.1 +1L$^zSerialGetSwizzled+2^HS.Local.CZ.OR.ROUTER.Service.Data.KeyProperty.1 +3G$^%AddToSaveSet+5^HS.Local.CZ.OR.ROUTER.Service.Data.KeyProperty.1 +16$^%BuildObjectGraph+1^%Library.RegisteredObject.1 +1?$^%Save+5^HS.Local.CZ.OR.ROUTER.Service.Data.KeyProperty.1 +1AD^ztest2+12^HS.Local.CZ.OR.ROUTER.Service.Data.KeyProperty.1 +X^@ +1

The whole purpose of the KeyProperty class is to be part of another class which is meant to replicate Ens.Request instances into memory and regenerate them later. We need this because even though the Ens.Request class is persistent, it is deleted during management data purges and there are some requests we need to keep/remake even after purge schedule. I have figured out saving literal and persistent properties, so now I need to figure out a way to do so with serial ones as well. This was the first and simplest impulse but there might be another obvious way to save any sort of serial object and retrieve it later.

Try this:

ClassMethod test2()
  set tKP = ..%New()
  set tIdentifierSerial ##class(HS.Message.AddUpdateHubRequest).%OpenId(21986071).Identifiers
  zw tIdentifierSerial
  set tKP.IsSerial = 1
  set tKP.IsList = 1
  set tSerial tIdentifierSerial.GetObjectNext(.tKey)
  for {
    if tKey="" {quit}
    do tKP.SerialList.InsertObject(tSerial)
    set tSerial tIdentifierSerial.GetObjectNext(.tKey)

I can provide a solution for this but it comes with warnings - SQL doesn't do well with polymorphic embedded objects. If that isn't an issue then consider using a factory pattern. You must define a base class that extends %SerialObject and it must be instantiable. Then, override %OnNew() to dispatch to the concrete class that you wish to instantiate. Perhaps pass in a type argument. That %OnNew() can return an OREF (or a %Status if an error occurs).

If you are interested in this model then I can provide a small demo.