Question
Javier Gonzalez · Jun 6, 2017

Generic Class in COS

Hello everyone,

 

I'm doing a REST service for integration between 2 systems. The system that invokes me expects a response object where only one of its attributes changes. It could be something like this:

Class ResponseCustomer  {
    Property Code As %String;
    Property Info As Customer;
}
Class ResponseAppointment {
    Property Code As %String;
    Property Info As Appointment;
}
Class ResponseCancellation  {
​    Property Code As %String;
    Property Info As %String;
}

To avoid creating almost identical objects for each of the methods of my REST service, I had thought to implement it with generic classes known in other languages like .NET or JAVA that allow to be able to define at run time the type of that parameter. In addition, that response must be able to be transformed into JSON. I imagine something like this (in pseudo code / COS / .NET):

 

Class Response <T> {
​    Property Code As %String;
    Property Info As T;
}

 

// Create an answer to create a new Customer
Set responseCustomer = ## class (Response <Customer>). New ()

// Create an answer to reserve a room
Set responseAppointment = ## class (Response <Appointment>).% New ()

​// Create an answer to cancel a reservation
Set responseCancelation = ## class (Response <String>).% New ()

 

Is this possible in COS?. I haven't found a similar question about this in the community.

My attempts have been with the use of %Library.DynamicObject but when creating the JSON, the dynamic part isn't serialized.

Thanks

0
0 454
Discussion (3)4
Log in or sign up to continue

Hi Javier,

COS does not have a Generics implementation, mainly as its a loosely/duck typed language.

You can however write generic code without needing Generics.

Make your property a base class of your Info classes, this can be %RegisteredObject...

Class Response Extends %RegisteredObject  {
​    Property Code As %String;
    Property Info As %RegisteredObject;
}


You can now assign any valid object to that property at run time.

You won't be able to assign a string to this property, so create a class with a single property of type string that you can assign it to.

Try that and if you get stuck with the JSON serialisation then post back the code that is not working.

Sean.

Thanks Sean,

 

With %RegisteredObject occurring same as with %Library.DynamicObject, the dynamic attribute is not serialized.

The REST service architecture is a BusinessService that invokes a BusinessOperation. In this BO I have a block "code" that does the following:

set response.Value="OK" 
set response.Res.Message = "I am a message"    
set response.Res.Code = 999
set response.Res.Info = ##class(CitaResponseOMI360).%New()
set response.Res.Info.idCita = 1

 

Return to the BS, I perform the serialization with class:

##class(Ens.Utils.JSON).ObjectToJSONStream(tRsp, .tStream)

and return the result with:

do tStream.OutputToDevice ().

The result of invoking the  REST service from POSTMAN or SOAPUI is this:

{
  "Value": "OK",
  "Res": {
    "Code": "999",
    "Message": "I am a message"
  }
}

From the terminal this is the result

"{"_$c(13,10)_"    ""Value"":""OK"","_$c(13,10)_"    ""Res"": {"_$c(13,10)_"        ""Code"":""999"","_$c(13,10)_"        ""Message"":""I am a message"","_$c(13,10)_"        ""Info"": {"_$c(13,10)_"            ""idCita "":""1""}"_$c(13,10)_"    }"_$c(13,10)_"}"

 

In order to provide some detail, this is the definition of the classes involved:

Class Respuesta Extends (%SerialObject, %XML.Adaptor) {
Property Value As %String;
Property Res As RespuestaNodo;
}

 

Class RespuestaNodo Extends (%SerialObject, %XML.Adaptor) {
Property Code As %String;
Property Message As %String;
Property Info As %RegisteredObject;
}

 

Class CitaResponseOMI360 Extends Ens.Response
{
Property idCita As %String;
Property FechaHora As %TimeStamp;
Property agenda As AgendaOMI360;
Property paciente As PacienteOMI360;
Property observaciones As %String;
Property estado As %String;
​}

 

I thank your contribution because I am confused.

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
    ]
  }
}