- PVSM.RU - https://www.pvsm.ru -

Новые динамические объекты и поддержка JSON в InterSystems Caché

Dynamism of A Dog on a Leash (1912) Giacomo Balla [1]Вообще, в InterSystems Caché и динамические объекты, и поддержка JSON есть уже достаточно давно, но в версии 2016.1 они были переосмыслены, а код реализации переведён с COS уровня на уровень ядра/С, что позволило добиться существенного повышения производительности в этих областях. О том, что есть нового и как переходить (а также о том, как сохранить совместимость с предыдущими версиями) я расскажу в этой статье.

Возможности по работе с JSON

И начну с примера. Теперь такой синтаксис — работает и это самое большое нововведение в синтаксисе COS:

Set object = { "property": "val", "property2": 2, "property3": null }
Set array = [ 1, 2, "string", true ]

Как видите JSON теперь является полноправной частью COS. Что же происходит при подобном присвоении? Объект object становится экземпляром класса %Library.Object [2], а array является экземпляром класса %Library.Array [3]. Они оба являются динамическими объектами.

Динамические объекты

Динамические объекты в Cache были и раньше — в виде класса %ZEN.proxyObject [4], но теперь код перемещён в ядро, за счёт чего достигнут значительный прирост по скорости. Все классы динамических объектов наследуются от %Library.AbstractObject [5], который предоставляет следующую функциональность:

  • Получение объекта из JSON строки, потока, файла
  • Вывод объекта в формате JSON в строку или переменную, автоматическое определение формата вывода в зависимости от контекста
  • Запись объекта в формате JSON в файл
  • Запись объекта в глобал
  • Чтение объекта из глобала

Переход от %ZEN.proxyObject

Итак, вы хотите перейти от %ZEN.proxyObject [4] и различных наследников %Collection.AbstractIterator [6] к использованию наследников %Library.AbstractObject? Это несложно и есть несколько методов:

  • Если вас не интересует совместимость с версиями Caché, предшествующими 2016.1 то вдумчивый Ctrl+H — ваш вариант. Помните, что индексы в массивах теперь начинаются с нуля и к названиям системных методов нужно добавлять $
  • Используйте макросы, которые во время компиляции преобразуют код в нужный вид в зависимости от версии Caché. Я уже писал [7] на Хабре вводную статью про макросы и про пример [8] их использования
  • Используйте класс-абстракцию, который оборачивает соответствующие методы

Использование первого метода в общем-то очевидно, а вот на двух других остановимся поподробнее.

Макросы

Примерный код набора макросов, которые в зависимости от наличия %Library.AbstractObject работают либо с новым, либо с прежним классом динамических объектов.

Макросы

#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"))

Использование

Вот такой код:
 Set obj $$$NewDynObj
 Set 
obj.prop "val"
 
$$$DynObjToJSON(obj)
    
 
Set dtList $$$NewDynDTList
 Set 
= 1
 
$$$Insert(dtList,a)
 
$$$Insert(dtList,"a")
 
$$$ListToJSON(dtList)

В Cache версии 2016.1+ скомпилируется в int такой код:
 set obj ##class(%Library.Object).%New()
 
set obj.prop "val"
 
obj.$toJSON()
 
set dtList ##class(%Library.Array).%New()
 
set = 1
 
do dtList.$push(a)
 
do dtList.$push("a")
 
dtList.$toJSON()

А в предыдущих версиях в:
 set obj ##class(%ZEN.proxyObject).%New()
 
set obj.prop "val"
 
do obj.%ToJSON()
 
set dtList ##class(%Library.ListOfDataTypes).%New()
 
set = 1
 
do dtList.Insert(a)
 
do dtList.Insert("a")
 
do ##class(%ZEN.Auxiliary.jsonProvider).%ObjectToJSON(dtList)

Класс абстракция

Альтернативным вариантом является создание класса, абстрагирующего используемый динамический объект, например:

Класс Utils.DynamicObject

Class Utils.DynamicObject Extends %RegisteredObject
{
/// Свойство, хранящее настоящий динамический объект
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
}
/// Получение динамических свойств
Method 
%DispatchGetProperty(pProperty As %String) [ Final ]
{
    
Quit ..obj.%DispatchGetProperty(pProperty)
}
/// Установка динамических свойств
Method 
%DispatchSetProperty(pProperty As %StringpValue As %String) [ Final ]
{
    
Do ..obj.%DispatchSetProperty(pProperty,pValue)
}
/// Конвертируем в JSON
Method 
ToJSON() [ Final ]
{
    
#if $$$comClassDefined("%Library.AbstractObject")
        
Write ..obj.$toJSON()
    
#else
        Do 
..obj.%ToJSON()
    
#endif
}
}

Использование полностью аналогично обычному классу:

 Set obj ##class(Utils.DynamicObject).%New()
 
Set obj.prop "val"
 
Do obj.ToJSON()

Что выбирать

Решать вам. Вариант с классом выглядеть привычнее, вариант с макросами будет несколько быстрее за счёт отсутствия промежуточных вызовов. Для проекта MDX2JSON [9] я выбрал вариант с макросами. Переход прошел быстро и безболезненно [10].

Производительность JSON

Скорость генерации JSON возросла на порядок. В проекте MDX2JSON есть тесты скорости [11] генерации JSON. Скачайте и убедитесь!

Выводы

Новые динамические объекты и улучшения в поддержке JSON позволяют ускорить работу ваших приложений.

Ссылки

» Документация [12]
» Статья на community.intersystems.com о JSON [13]
» Класс Utils.DynamicObject [14]

Автор: InterSystems

Источник [15]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/programmirovanie/117347

Ссылки в тексте:

[1] Image: http://habrahabr.ru/company/intersystems/blog/268767/

[2] %Library.Object: http://docs.intersystems.com/cache20161/csp/documatic/%25CSP.Documatic.cls?PAGE=CLASS&LIBRARY=%25SYS&CLASSNAME=%25Library.Object

[3] %Library.Array: http://docs.intersystems.com/cache20161/csp/documatic/%25CSP.Documatic.cls?PAGE=CLASS&LIBRARY=%25SYS&CLASSNAME=%25Library.Array

[4] %ZEN.proxyObject: http://docs.intersystems.com/cache20161/csp/documatic/%25CSP.Documatic.cls?PAGE=CLASS&LIBRARY=%25SYS&CLASSNAME=%25ZEN.proxyObject

[5] %Library.AbstractObject: http://docs.intersystems.com/cache20161/csp/documatic/%25CSP.Documatic.cls?PAGE=CLASS&LIBRARY=%25SYS&CLASSNAME=%25Library.AbstractObject

[6] %Collection.AbstractIterator: http://docs.intersystems.com/cache20161/csp/documatic/%25CSP.Documatic.cls?PAGE=CLASS&LIBRARY=%25SYS&CLASSNAME=%25Collection.AbstractIterator

[7] писал: http://habrahabr.ru/company/intersystems/blog/258081/

[8] пример: http://habrahabr.ru/company/intersystems/blog/258805/

[9] MDX2JSON: https://github.com/intersystems-ru/Cache-MDX2JSON

[10] быстро и безболезненно: https://github.com/intersystems-ru/Cache-MDX2JSON/pull/48

[11] тесты скорости: https://github.com/intersystems-ru/Cache-MDX2JSON/blob/master/MDX2JSON/Tests.cls.xml#L123

[12] Документация: http://docs.intersystems.com/cache20161/csp/docbook/DocBook.UI.Page.cls?KEY=GJSON

[13] Статья на community.intersystems.com о JSON: https://community.intersystems.com/post/introducing-new-json-capabilities-cach%C3%A9-20161

[14] Класс Utils.DynamicObject: https://gist.githubusercontent.com/eduard93/b39aaaba759be255db0a/raw/4763586cbf94aa8532df5f8ebfbe6d4d0f302741/DynamicObject.xml

[15] Источник: https://habrahabr.ru/post/272059/