
In general, in InterSystems Caché, both dynamic objects and JSON support have been around for quite some time, but in version 2016.1 they were rethought, and the implementation code was transferred from the COS level to the kernel / C level, which made it possible to significantly improve performance in these areas. That is new and how to go (as well as how to maintain compatibility with previous versions) I will discuss in this article.
JSON features
And start with an example. Now this syntax works and this is the biggest innovation in COS syntax:
Set object = { "property": "val", "property2": 2, "property3": null } Set array = [ 1, 2, "string", true ]
As you can see, JSON is now a full part of COS. What happens with such an assignment? The object object becomes an instance of the class
% Library.Object , and array is an instance of the class
% Library.Array . They are both dynamic objects.
Dynamic objects
Dynamic objects in Cache were in the past - in the form of the
% ZEN.proxyObject class, but now the code has been moved to the core, due to which a significant increase in speed has been achieved. All classes of dynamic objects are inherited from
% Library.AbstractObject , which provides the following functionality:
')
- Getting an object from a JSON string, stream, file
- Outputting an object in JSON format to a string or variable, automatically determining the output format depending on the context
- Writing an object in JSON format to a file
- Write object to global
- Reading an object from the global
Transition from% ZEN.proxyObject
So, do you want to move from
% ZEN.proxyObject and various heirs of
% Collection.AbstractIterator to use the heirs of% Library.AbstractObject? This is easy and there are several methods:
- If you are not interested in compatibility with Caché versions prior to 2016.1, then a thoughtful Ctrl + H is your option. Remember that indices in arrays now start from zero and you need to add $ to the names of system methods
- Use macros that, during compilation, convert the code into the desired view depending on the version of Caché. I already wrote on Habré an introductory article about macros and about an example of their use
- Use an abstraction class that wraps the appropriate methods.
The use of the first method is obvious in general, but we’ll dwell on the other two in more detail.
Macros
Sample code for a set of macros that, depending on the availability of% Library.AbstractObject, work with either the new or the old class of dynamic objects.
Macros#if $$$ comClassDefined ( "% Library.AbstractObject" )
#define NewDynObj ## class (% Object).% New ()
#define NewDynDTList ## class (% Array).% New ()
#define NewDynObjList $$$ NewDynDTList
#define Insert (% obj,% element) do% obj. $ push (% element)
#define DynObjToJSON (% obj) w% obj. $ toJSON ()
#define ListToJSON (% obj) $$$ DynObjToJSON ( % obj )
#define ListSize (% obj) % obj. $ size ()
#define ListGet (% obj,% i) % obj. $ get (% i-1)
#else
#define NewDynObj ## class (% ZEN.proxyObject).% New ()
#define NewDynDTList ## class (% ListOfDataTypes).% New ()
#define NewDynObjList ## class (% ListOfObjects).% New ()
#define Insert (% obj,% element) do% obj.Insert (% element)
#define DynObjToJSON (% obj) do% obj.% ToJSON ()
#define ListToJSON (% obj) do ## class (% ZEN.Auxiliary.jsonProvider).% ObjectToJSON (% obj)
#define ListSize (% obj) % obj.Count ()
#define ListGet (% obj,% i) % obj.GetAt (% i)
#endif
#define IsNewJSON ## Expression ($$$ comClassDefined ( "% Library.AbstractObject" ) )
UsingHere is the code:
Set obj = $$$ NewDynObj
Set obj . prop = "val"
$$$ DynObjToJSON ( obj )
Set dtList = $$$ NewDynDTList
Set a = 1
$$$ Insert ( dtList , a )
$$$ Insert ( dtList , "a" )
$$$ ListToJSON ( dtList )
In Cache version 2016.1+, the following code will be compiled into int:
set obj = ## class ( % Library.Object ). % New ()
set obj . prop = "val"
w obj . $ toJSON ()
set dtList = ## class ( % Library.Array ). % New ()
set a = 1
do dtList . $ push ( a )
do dtList . $ push ( "a" )
w dtList . $ toJSON ()
And in previous versions in:
set obj = ## class ( % ZEN.proxyObject ). % New ()
set obj . prop = "val"
do obj . % ToJSON ()
set dtList = ## class ( % Library.ListOfDataTypes ). % New ()
set a = 1
do dtList . Insert ( a )
do dtList . Insert ( "a" )
do ## class ( % ZEN.Auxiliary.jsonProvider ). % ObjectToJSON ( dtList )
Class abstraction
An alternative is to create a class that abstracts the dynamic object used, for example:
Class Utils.DynamicObjectClass Utils.DynamicObject Extends% RegisteredObject
{
/// Property that stores a real dynamic object
Property obj;
Method % OnNew () As% Status
{
#if $$$ comClassDefined ( "% Library.AbstractObject" )
Set .. obj = ## class ( % Object ). % New ()
#else
Set .. obj = ## class ( % ZEN.proxyObject ). % New ()
#endif
Quit $$$ OK
}
/// Get dynamic properties
Method % DispatchGetProperty ( pProperty As% String ) [ Final ]
{
Quit .. obj . % DispatchGetProperty ( pProperty )
}
/// Setting dynamic properties
Method % DispatchSetProperty ( pProperty As% String , pValue As% String ) [ Final ]
{
Do .. obj . % DispatchSetProperty ( pProperty , pValue )
}
/// Convert to JSON
Method ToJSON () [ Final ]
{
#if $$$ comClassDefined ( "% Library.AbstractObject" )
Write .. obj . $ toJSON ()
#else
Do .. obj . % ToJSON ()
#endif
}
}
The use is completely analogous to the usual class:
Set obj = ## class ( Utils.DynamicObject ). % New ()
Set obj . prop = "val"
Do obj . ToJSON ()What to choose
You decide. The variant with the class looks more familiar; the variant with macros will be somewhat faster due to the absence of intermediate calls. For the
MDX2JSON project
, I chose the
macro option. The transition was
quick and painless .
JSON performance
JSON generation speed increased by an order. In the project MDX2JSON there are
tests for generating JSON
speed . Download and make sure!
findings
New dynamic objects and improvements in JSON support allow you to speed up your applications.
Links
»
Documentation»
Community.intersystems.com article on JSON»
Class Utils.DynamicObject