Нові динамічні об'єкти і підтримка JSON в InterSystems Caché

Dynamism of A Dog on a Leash (1912) Giacomo BallaВзагалі, 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, а array є екземпляром класу %Library.Array. Вони обидва є динамічними об'єктами.

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

  • Отримання об'єкта з JSON рядка, потоку, файлу
  • Висновок об'єкта в форматі JSON в рядок або змінну, автоматичне визначення формату виводу в залежності від контексту
  • Запис об'єкта в форматі JSON в файл
  • Запис об'єкта в глобал
  • Читання об'єкта з глобал
Перехід від %ZEN.proxyObject
Отже, ви хочете перейти від %ZEN.proxyObject і різних спадкоємців %Collection.AbstractIterator до використання спадкоємців %Library.AbstractObject? Це нескладно і є кілька методів:

  • Якщо вас не цікавить сумісність з версіями Caché, попередніми 2016.1 то вдумливий Ctrl+H — ваш варіант. Пам'ятайте, що індекси в масивах тепер починаються з нуля і до назв системних методів потрібно додавати $
  • Використовувати макроси, які під час компіляції перетворять код в потрібний вигляд в залежності від версії Caché. Я вже писав на Хабре вступну статтю про макроси і про приклад використання
  • Використовуйте клас-абстракцію, який обертає відповідні методи
Використання першого методу в загальному-то очевидно, а на двох інших зупинимося детальніше.

Макроси
Зразковий код набору макросів, які в залежності від наявності %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 a = 1
 $$$Insert(dtList,a)
 $$$Insert(dtList,"a".)
 $$$ListToJSON(dtList)

У Cache версії 2016.1+ відбудеться створення в 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()

А в попередніх версіях:
 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)

Клас абстракція
Альтернативним варіантом є створення класу, абстрагирующего використовується динамічний об'єкт, наприклад:

Клас Utils.DynamicObjectClass 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 %String, pValue 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 я вибрав варіант з макросами. Перехід пройшов швидко і безболісно.

Продуктивність JSON
Швидкість генерації JSON зросла на порядок. У проекті MDX2JSON є тести швидкості генерації JSON. Скачайте і переконайтеся!

Висновки
Нові динамічні об'єкти та покращення в підтримці JSON дозволяють прискорити роботу ваших додатків.

Посилання
» Документация
» Стаття community.intersystems.com про JSON
» Клас Utils.DynamicObject

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.