Written by

Sales Account Manager at InterSystems
Question David Reche · Feb 16, 2016

Manage data types with %ZEN.proxyObject and Ens.Util.JSON.ObjectToJSONStream()

Hello,

I am trying to use %ZEN.proxyObject to send out in JSON format so I do:

    set tProxyRequest = ##class(%ZEN.proxyObject).%New()
    set tProxyRequest.notanumber = "28001"
    set tProxyRequest.aboolean = "true"
    
    set tBody = ##class(%GlobalCharacterStream).%New()
    do ##class(Ens.Util.JSON).ObjectToJSONStream(tProxyRequest,.tBody,"aelotwu")
    w tBody.Read()

and I get:

{
        "aboolean":"true",
        "notanumber":28001
}

But I want this:

{
        "aboolean":true,
        "notanumber":"28001"
}

Help please !

Comments

Eduard Lebedyuk · Feb 17, 2016

It can be done in 2016.1 FT, see this post. To do it in older versions, inherit from system classes.

Alternatively you can define a persistent class with properties of required types and transform it into json.

P.S. As a hack: JSON consumer can sometimes accept 1/0 in place of true/false, so you can try to use this values.

0
Stefan Wittmann  Feb 17, 2016 to Eduard Lebedyuk

What Eduard means is that you can create a registered class, with typed properties (a boolean and a numeric in your case), populate it and then transform to JSON using the jsonProvider.

That is the only valid approach to control JSON types in versions before 2016.1. Everything else is a hack.

0
David Reche · Feb 17, 2016

Stefan. I used a registered class with typed properties and doesn't works frown

I made this test class:

Class test.DummyClass Extends %RegisteredObject
{

Property notanumber As %String;

Property aboolean As %Boolean;

Method outout2JSON()
{
    set tProxyRequest = ##class(%ZEN.proxyObject).%New()
    set tProxyRequest.notanumber = ..notanumber
    set tProxyRequest.aboolean = ..aboolean
    
    set tBody = ##class(%GlobalCharacterStream).%New()
    do ##class(Ens.Util.JSON).ObjectToJSONStream(tProxyRequest,.tBody,"aelotwu")
    w tBody.Read()
}

ClassMethod Test()
{
    set dummy = ##class(test.DummyClass).%New()
    set dummy.notanumber = "28001"
    set dummy.aboolean = 1
    do dummy.outout2JSON()
}

}

And this is the result sad:

USER>do ##class(test.DummyClass).Test()
{
        "aboolean":1,
        "notanumber":28001
}
USER>w $zv
Cache for Windows (x86-64) 2015.2.1 (Build 705U) Mon Aug 31 2015 16:45:59 EDT

 

What's wrong?

Thanks

0
Eduard Lebedyuk  Feb 17, 2016 to David Reche

I modified your code like this:

Class test.DummyClass Extends %RegisteredObject
{

Property notanumber As %String;

Property aboolean As %Boolean;

/// do ##class(test.DummyClass).Test()
ClassMethod Test()
{
    
    set dummy = ..%New()
    set dummy.notanumber = "28001"
    set dummy.aboolean = 1
    do ##class(%ZEN.Auxiliary.jsonProvider).%ObjectToJSON(dummy,,,"aelotw")
}
}

Terminal output:

>do ##class(test.DummyClass).Test()
{
        "notanumber":"28001",
        "aboolean":true
}
0
Stefan Wittmann  Feb 17, 2016 to Eduard Lebedyuk

Thanks Eduard for posting the working solution. The reason why this works is that you actually create an object of your registered class and assign the values. The jsonProvider API can now walk the object and project the types properly.

In the non-working example, you created a zenProxyObject and assigned the values on your own. The zenProxyObject just deals with auto-types and guesses the best possible type fit. As you see, this guess can't be correct all the time as in this case.

If you want to create a JSON structure with specific types, either use a) the new JSON support in 2016.1 (recommended if 2016.1 is available) or b) create a subclass from %RegisteredObject and use the jsonProvider API.

0
David Reche · Feb 17, 2016

PERFECT !!

I see now my mistake smiley

I fixed the code like this:

Class test.DummyClass Extends %RegisteredObject
{
Property notanumber As %String;
Property aboolean As %Boolean;
Method outout2JSON()
{
    set tBody = ##class(%GlobalCharacterStream).%New()
    do ##class(Ens.Util.JSON).ObjectToJSONStream(##this,.tBody,"aelotw")
    w tBody.Read()
}
ClassMethod Test()
{
    set dummy = ##class(test.DummyClass).%New()
    set dummy.notanumber = "28001"
    set dummy.aboolean = 1
    do dummy.outout2JSON()
}
}
 

And now:

USER>do ##class(test.DummyClass).Test()
{
        "notanumber":"28001",
        "aboolean":true
}

0
Joel Solon  Feb 17, 2016 to David Reche

Small comment: use $this instead of ##this, even though they compile to the same thing.

0
David Reche · Feb 17, 2016

But ... another problem...

What if a need to send a JSON object with properties with underscore characters like "not_a_number"?

Is because that I used %ZEN.proxyObject...

0
Stefan Wittmann  Feb 17, 2016 to David Reche

You can define quoted property names, e.g.:

Property "not_a_number" As %String;

same for runtime properties:

set object."not_a_number" = "28456"

0
Neerav Verma  Dec 24, 2020 to Stefan Wittmann

How do we display Date properties as display format and not internal?

0