Question
· Jun 1, 2017

Casting JSON

I'm doing a REST service. A method has as body parameter a JSON corresponding to a class A.

In my production I have class A so that I retrieve the parameters using a dynamic object, such that:

Set body = ##class(%DynamicObject).%FromJSON(%request.Content)
Set myObjectA = ##class(A).%New()
Set myObjectA.Id = body.Id
Set myObjectA.Name = body.Name
Set myObjectA.Date = body.Date
Set myObjectA.Salary = body.Salary

I would like to know if I can avoid doing the manual mapping, doing a casting, since I am sure that FromJSON will return a class A. Something like this:

Set myObjectA = ##class(A).%FromJSON(%request.Content)

 

Any suggestions? Thanks.

Discussion (9)1
Log in or sign up to continue

Hi Javier

I did this by implementing a fromJSON method on each class, which allows me to do what you describe.  By moving this to the persistent class, I don't have to worry about instantiating or accessing the object, but can just apply a JSON update to an object

The details are in https://community.intersystems.com/post/lets-write-angular-1x-app-cach%C3%A9-rest-backend-part-9

Hope this helps
Chris

Hello Chris,

Thanks for the reply. I am looking for a native COS solution to avoid working with own classes. Maybe you may be interested in the solution that Eduard has provided. I do not know what limitations I may have (for example with the dates, which Sean has commented). If the community sees any, they could comment on it.

Regards !!

One small caveat to consider. Whilst JSON does not have a date type, there is a mismatch between the preferred W3C date that most people use and the internal date format of Cache.

You will find with both of the suggestions that you will still need to do a last minute translation of these dates before you call %Save(), otherwise you will get a save error.

Hi Javier, 

A some years ago I wrote a class to copy from to object. Is a simple data tranformation that reads the definition of source object and try to set the target object.

Is a very simple copy, without care about with types, I use for save time when I need to copy many properties of source object  to target object.

Recently I adapted the code to consider the source objects that are dynamic objects.

Works similar to the method from JSON that Chris wrote in the post.

The code was written in portuguese (Brazil) I hope can be useful to you.

/// <p>
/// <b>2012-03-01 - Cristiano José da Silva</b><br/>
/// Classe utilizada para copiar todas propriedades de um objeto origem para um objeto destino.<br/>
/// Esse método não se comporta como o %ConstructClone, pois ele copia os dados de origem para um 
/// destino mesmo sendo de tipos diferentes. Ele foi implementado para poder copiar objetos de tipos 
/// diferentes mas com a mesma definição.<br/>
/// A definição mandatória é a da origem, isto é, caso o objeto destino possua um referência para outro
/// objeto, e no objeto origem essa mesma propriedade seja uma string, o objeto destino receberá uma string 
/// no lugar de uma referência.<br/>
/// Caso queira ignorar uma ou mais propriedade passa-las em no array pProriedadesAIgnorar indexao pelo nome 
/// da propriedade Ex:
/// <example>
/// Set tPropriedadesIgnoradas("Prop1") = 1
/// Set tPropriedadesIgnoradas("PropN") = 1
/// </example>
/// <strong>
/// Os nomes das propriedades devem ser exatamente iguais nas duas classes.</br>
/// Os tipos também devem ser os mesmos para evitar erros.
/// </strong><br/>
/// </p>
Class cjs.dt.CopiarObjetoDT Extends Ens.DataTransform [ ProcedureBlock ]
{

ClassMethod Transform(pSource As %RegisteredObject, pTarget As %RegisteredObject, ByRef pProriedadesIgnoradas) As %Status
{
    #Dim tException As %Exception.General
    #Dim tStatus As %Status = $System.Status.OK() 
    Try
    {
        If ($IsObject($Get(pSource)) '= 1) 
        {
            Set tStatus = $System.Status.Error(5001, "Objeto origem é obrigatório")
            //
            Quit
        }
        If ($IsObject($Get(pTarget)) '= 1) 
        {
            Set tStatus = $System.Status.Error(5001, "Objeto destino é obrigatório")
            //
            Quit
        }
        // Trata Objetos Dinâmicos (JSON)
        If (pSource.%IsA("%Object"))
        {
            #Dim tIterator As %Iterator.AbstractIterator = pSource.$getIterator()
            #Dim tChave As %String = ""
            #Dim tValor As %String = ""
            //
            While (tIterator.$getNext(.tChave, .tValor))
            {
                If ($Data(pProriedadesIgnoradas))
                {
                    Continue:($Get(pProriedadesIgnoradas(tChave), 0))
                }
                Set $Property(pTarget, tChave) = tValor
            }
        }
        Else
        {
            Set tDefinicaoClasse                   = ##Class(%Dictionary.CompiledClass).%OpenId(pSource.%ClassName(1))
            #Dim tPropriedades As %ArrayOfObjects  = tDefinicaoClasse.Properties
            #Dim tIndicePropriedade As %Integer    = 0
            //
            For tIndicePropriedade = 1 : 1 : tPropriedades.Count()
            { 
                #Dim tPropriedade As %Dictionary.CompiledProperty = tPropriedades.GetAt(tIndicePropriedade)
                //
                Continue:(tPropriedade.Private || tPropriedade.ReadOnly || tPropriedade.Calculated || tPropriedade.Transient)
                //
                #Dim tNomePropriedade As %String = tPropriedade.Name
                //
                If ($Data(pProriedadesIgnoradas))
                {
                    Continue:($Get(pProriedadesIgnoradas(tNomePropriedade), 0))
                }
                Set $Property(pTarget, tNomePropriedade) = $Property(pSource, tNomePropriedade)
            }
        }
    }
    Catch (tException)
    {
        Set tStatus = tException.AsStatus()
    }
    Return tStatus
}

ClassMethod Teste()
{
    Write !, "Teste ambos sendo um objeto registrado", !
    //
    Set tOrigem             = ##Class(Ens.StringContainer).%New()
    Set tOrigem.StringValue = "Ambos registrado."
    Set tDestino            = ##Class(Ens.StringContainer).%New()
    //
    Do ..Transform(tOrigem, tDestino)
    //
    Write tDestino.StringValue, !
    Write !, "Teste da origem sendo um objeto dinâmico", !
    //
    Set tOrigem             = ##Class(%Object).%New()
    Set tOrigem.StringValue = "Origem dinâmico"
    Set tDestino            = ##Class(Ens.StringContainer).%New()
    //
    Do ..Transform(tOrigem, tDestino)
    //
    Write tDestino.StringValue, !
    Write !, "Teste da destino sendo um objeto dinâmico", !
    //
    Set tOrigem             = ##Class(Ens.StringContainer).%New()
    Set tOrigem.StringValue = "Destino dinâmico"
    Set tDestino            = ##Class(%Object).%New()
    //
    Do ..Transform(tOrigem, tDestino)
    //
    Write tDestino.StringValue, !
    Write !, "Teste ambos sendo um objeto dinâmico", !
    //
    Set tOrigem             = ##Class(%Object).%New()
    Set tOrigem.StringValue = "Ambos dinâmico"
    Set tDestino            = ##Class(%Object).%New()
    //
    Do ..Transform(tOrigem, tDestino)
    //
    Write tDestino.StringValue, !
}

}

Only if it's a collection property:

Class Utils.DynArrayProp Extends %Persistent
{

Property A;

Property B As list Of %Integer;

/// do ##class(Utils.DynArrayProp).Test()
ClassMethod Test()
{
	Do ..%KillExtent()
	Set json = "{""A"":123,""B"":[1,2,3]}"
	Set dynamicObject = {}.%FromJSON(json)
	Set object = ##class(%ZEN.Auxiliary.altJSONProvider).%UnpackObjectToCOSObject(dynamicObject, $classname())
	Set sc = object.%Save()
	ZW sc,object,^Utils.DynArrayPropD
}
}