Pesquisar

Artigo
· Ago. 15 8min de leitura

Beginner's Guide to %JSON and Dynamic Entities in IRIS

Dynamic Entities (objects and arrays) in IRIS are incredibly useful in situations where you are having to transform JSON data into an Object Model for storage to the database, such as in REST API endpoints hosted within IRIS. This is because these dynamic objects and arrays can easily serve as a point of conversion from one data structure to the other.

Dynamic Objects

Dynamic Objects are very similar to the standard ObjectScript object model you get when you create a new instance of a class object, but with some key differences:

  • Dynamic objects have no real type, other than being a DynamicObject or DynamicArray
  • They cannot inherit or be morphed directly into a class object
  • They're much easier to create and come with a host of built-in functions for managing and manipulating them

To create a dynamic object, it's as easy as:

set obj = {}

We now have "obj" as a dynamic object. If you've used Javascript/Typescript before, this will look very similar. This connection to Javascript becomes very important when talking about arrays later.

With our new dynamic object, we can add and manipulate properties of this object just like we can with class objects, which the added flexibility of not having to predefine properties that the object can use.

set obj.message = "This is a test message"
set obj.num = 4
set obj.test = 1

So far, this all looks similar to the standard ObjectScript model. Here's where things get interesting:

set obj.subobj = {}

Now, we've created a completely new dynamic object within our existing dynamic object, with all the same functionality as the existing one.

set obj.subobj.message = "This is a message on the sub-object."

As you can see, we can string the dot notation together to access properties of sub-objects on a dynamic object. We can also assign dynamic objects as sub-objects on an existing object:

set obj1 = {}
set obj1.message = "This is object 1"
set obj2 = {}
set obj2.message = "This is a different object, object 2"
// Set the "subobj" property of obj1 to the entire obj2 object
set obj1.subobj = obj2

// Set the "obj1" on the original "obj" object to the entire "obj1" object, which
// also now includes "obj2"
set obj.obj1 = obj1

You can also use the %Set(), %Get(), and %Remove() methods on the object (and arrays) to manipulate the data stored in object properties, which is especially useful for properties that have long names. %Remove() is the only real way to remove a property or element from a dynamic object or array.

Dynamic Arrays

We've talked about dynamic objects a lot up to this point, but what about dynamic arrays?

These work very similarly to the objects. To create one:

set array = [1,2,"three",true]

Notice that rather than creating the empty array first and then adding items to it, I've given it a starting value. This can also be done with objects:

set test = {"message":"Test message","num":4}

Also notice that when defining objects this way, the property names must be quoted or else you will get a syntax error.

NOTE: It is CRITICAL to understand that dynamic arrays will be 0-indexed, as opposed to standard IRIS arrays which are 1-indexed. All dynamic arrays start at index 0.

Getting back to arrays, once you have your array you have access to some standard methods for interacting with it, such as:

  • %Push(element) - Add a new element to the end of the existing array
  • %Pop() - Remove and return the last element in the array
  • %Size() - Get the number of elements currently in the array
  • %IsDefined(element number) - Returns whether or not the given key (array index number) exists in the array.

 

Combining Objects and Arrays

As is standard in JSON, you can have an object with properties containing arrays and arrays that contain a set of objects. As shown above when setting sub-objects onto other objects, you can do the same for dynamic arrays as well.

Adding an array to an object:

set obj = {"message":"This is a message"}
set array = [1,2,3,4,5]

set obj.array = array

Adding an object to an array:

set newobj = {"message1":"Message 1", "message2":"Message 2"}
set newarray = [1,2,3]
do newarray.%Push(newobj)

 

JSON Conversion and Formatting

More than likely, in a real setting these entities are likely to be quite large and difficult to mentally keep track of what properties are where, and what is in an array.

For instance, let's say we have this code:

set testObj = {}
set testObj.message = "This is a test message"
set testObj.num = 4
set subobj = {}
set subobj.message = "This is a message on the sub-object."
set testObj.subobj = subobj

set array = [1,2,3,4,5]
set newobj = {"six":6}
do array.%Push(newobj)

set testObj.array = array

There is a lot going on here with a mix of arrays and objects that make it difficult to mentally map out. To help with this, we can make use of the built in %ToJSON() method which will convert the object into a properly formatted JSON string:

write testObj.%ToJSON()

Which will produce this output:

{"message":"This is a test message","num":4,"subobj":{"message":"This is a message on the sub-object."},"array":[1,2,3,4,5,{"six":6}]}

This is a good start, but it's not very readable. Let's have a look at the %JSON.Formatter class, which has a Format method. To use this, we can create a new instance of %JSON.Formatter, and then call the Format method which takes the JSON string as an input.

set formatter = ##class(%JSON.Formatter).%New()
do formatter.Format(testObj.%ToJSON())

Which gives us a nicely expanded, properly formatted JSON object display:

{
  "message":"This is a test message",
  "num":4,
  "subobj":{
    "message":"This is a message on the sub-object."
  },
  "array":[
    1,
    2,
    3,
    4,
    5,
    {
      "six":6
    }
  ]
}

 

 

Working with True/False in Dynamic Objects

Standard IRIS does not understand the keywords "true" or "false" typically. To represent these values in IRIS ObjectScript, we must use 1 or 0. 

However, when working with dynamic entities, you begin crossing more into Javascript territory which uses "true" and "false" just fine, but there are some limitations.

When defining an object or array, if you're working with a value or set of values contained within the [ ] or { }, then you can set properties or elements as "true" or "false" with no problem. These values will be represented as the Javascript representation of true and false when defined in this way. Outside of that context, IRIS will convert these values to 1 or 0 when displaying them in an ObjectScript context.

If, however, you're using dot syntax to manually set a property on an object, you can only use the IRIS syntax of 1 or 0, otherwise IRIS will throw a syntax error.This has the unfortunate side effect that the Javascript representation sees 1 and 0 as valid values, but with a very different meaning than what may be intended.

To work around this, you can use the %Set(key, default, type) method which takes the key you want to set (object property or array element), an optional default value, and then a string type value, which can be "null", "string", "number", or most importantly, "boolean". When using the %Set() method with a type of "boolean", the 1 and 0 values are then converted to the Javascript representation of "true" and "false". The %Push() method on dynamic arrays can also take a type parameter in the same way. Arrays also have the %Set() method to set a value of a given array index.

See the documentation on %Set() and %Push() for details.

 

Converting a JSON string into a dynamic object or array

So, this is all great when we want to create a JSON object to perhaps send as a response to a REST API request. Simply setup a dynamic object with all the appropriate properties, and write it out and the downstream system will see it as a proper JSON object.

But, what about that initial request that has a POST body in JSON format? How can that be converted from the JSON string into a dynamic object or array?

Similar to the %ToJSON() method we used earlier, there is also a %FromJSON() method which takes the JSON string as the parameter and sets up the object/array according to the JSON definition.

For example, if we use the same JSON string we got earlier:

set jsonString = {"message":"This is a test message","num":4,"subobj":{"message":"This is a message on the sub-object."},"array":[1,2,3,4,5,{"six":6}]}

We can convert this into an object like this:

set obj = {}.%FromJSON(jsonString)

This will set the "obj" variable to be the dynamic object representation of the given JSON.

Now, let's say we have an array of books that we need to process:

[
	{
		"id": 1,
		"title": "ObjectScript for Dummies",
		"type": "paperback"
	},
	{
		"id": 2,
		"title": "Intermediate ObjectScript",
		"type": "paperback",
	},
	{
		"id": 3,
		"title": "Advanced ObjectScript",
		"type": "hardback",
	},
	{
		"id": 4,
		"title": "IRIS Embedded Python and You",
		"type": "paperback",
	},
]

 Since we're getting this data from another system, we have no way of knowing how many elements will be in this list when we get it. Thankfully, both dynamic arrays and objects are iterable! 

We can use the %GetIterator() method coupled with the %GetNext(.key,.value) method to iterate through each object in the array, or each top-level property of an object, to access the individual data.

set booksJSON = set booksJSON = "[{""id"": 1,""title"": ""ObjectScript for Dummies"",""type"": ""paperback""},{""id"": 2,""title"": ""Intermediate ObjectScript"",""type"": ""paperback""},{""id"": 3,""title"": ""Advanced ObjectScript"",""type"": ""hardback""},{""id"": 4,""title"": ""IRIS Embedded Python and You"",""type"": ""paperback""}]"
set booksArray = [].%FromJSON(booksJSON)

set bookIter = booksArray.%GetIterator()

while bookIter.%GetNext(.key,.value) {
    write !,"Book ",value.id,": ",value.title," (",value.type,")"
}

Which presents our request payload in a nice display list:

Book 1: ObjectScript for Dummies (paperback)
Book 2: Intermediate ObjectScript (paperback)
Book 3: Advanced ObjectScript (hardback)
Book 4: IRIS Embedded Python and You (paperback)

 

Documentation

For more information about how these entities can be used, please read the IRIS guides and class documentation:

Creating and Modifying Dynamic Entities
%Library.DynamicObject
%Library.DynamicArray

1 Comment
Discussão (1)2
Entre ou crie uma conta para continuar
Pergunta
· Ago. 15

Removal of Provider.Individual.Addresses from Local.PD.DataGram.Main.Unified using %clearFields

Is there a way to Remove specific all Addresses from a Provider.Individual.Address before reinserting the Addresses from an HL7 message in Provider Directory?

Most fields we can call %clearFields() however since Addresses come from multiple locations we need to isolate and treat Addresses from this HL7 source as a snapshot.

Thanks

Scott

Discussão (0)1
Entre ou crie uma conta para continuar
Anúncio
· Ago. 15

[Demo Video] MyVitals: Connecting Wearable Health Data to EHRs with InterSystems

#InterSystems Demo Games entry


⏯️ MyVitals: Connecting Wearable Health Data to EHRs with InterSystems

MyVitals is a Personal Health Device Hub that bridges the gap between wearable health devices and clinical workflows. In this demo, we show how healthcare organizations can enroll patients, collect real-time data from personal devices via mobile apps, and seamlessly integrate that information into EHR systems using FHIR, HL7, and InterSystems technology. Built on IRIS for Health and FHIR repositories, MyVitals turns disconnected wearable data into actionable clinical insights—securely, efficiently, and at scale.

Presenters:
🗣 @Alberto Fuentes, Sales Engineer, InterSystems
🗣 @Pierre-Yves Duquesnoy, Senior Sales Engineer, InterSystems
🗣 @Luis Angel Pérez Ramos, Sales Engineer, InterSystems

🗳 If you like this video, don't forget to vote for it in the Demo Games!

Discussão (0)1
Entre ou crie uma conta para continuar
Artigo
· Ago. 15 4min de leitura

Por que precisamos de regras ou convenções de codificação, afinal?

Este ótimo artigo despertou recentemente algumas discussões privadas, e eu gostaria de compartilhar algumas reflexões próprias a partir dele.

A preocupação central se resume a: Por que precisamos de regras ou convenções de codificação, afinal? O que aconteceu com a maravilhosa era do programador-artista renascentista, trilhando seu próprio caminho, antes de ser substituído pelo artesão e agora (ainda pior) pela IA?
Resumindo, há alguns motivos pelos quais padrões e diretrizes de codificação são úteis — e o programador-artista renascentista não desapareceu totalmente.

Motivo 1: Hoje em dia, quando você está treinando um verdadeiro artista iniciante, começa pedindo para ele tentar colorir dentro das linhas.
Esse artista pode ser brilhante um dia — mas ainda não é.
Se você quer aprender a produzir arte, faz isso primeiro; depois, gradualmente, aprende técnicas e conceitos com os mestres; e então, se for realmente bom (ou com um pouco de sorte), estabelece seu próprio estilo e cria algo novo que o resto do mundo vai querer seguir.
Mas, para começar, é preciso seguir as regras.

 

(Obs.: encontrei isso na internet; nenhum dos meus filhos ainda possui tal habilidade.)

Razão 2: Se você está operando em uma área onde seu nicho de criatividade deve se encaixar ao lado de outros – algo como uma colcha de retalhos – então você precisa de algumas regras para seguir, ou a arte não funcionará porque as peças não se encaixam. Você pode concordar com a convenção de “todos fazemos quadrados de 40cm” ou então ter que trabalhar de forma ainda mais próxima e colaborativa com um monte de outras pessoas, o que o artista solitário que passa 4 anos pintando um teto pode não ser o melhor em fazer. (Especialmente se a gerência decidir que alguns outros artistas solitários precisam ser chamados para “ajudar”).

(O ChatGPT me deu isso. Desculpas a todos os artistas de verdade.)

Razão3: Se você está trabalhando em uma peça de arte de programação existente, particularmente antiga ou particularmente brilhante, você frequentemente enfrenta o desafio entre entender e abraçar a intenção original do autor e a elegância do design e da execução, ou simplesmente dizer “agora eu estou no comando, vamos jogar fora e fazer do meu jeito”. Vou dar um exemplo aqui: uma aplicação web em que trabalhei faz uso intenso de “páginas XML” onde você tem um trio de (pageName.xml, pageName.js, pageName.mac) que constituem uma página. O pageName.xml foi, até mais recentemente do que eu gostaria de admitir, provavelmente escrito neste estranho dialeto "WD-xsl" que se assemelha muito ao XSLT padrão, mas só funciona no Internet Explorer Microsoft Edge se ele estiver fingindo ser o IE5. O pageName.js provavelmente contém a palavra "frame" cerca de 40 vezes mais do que seria confortável. O pageName.mac provavelmente está cheio de sintaxe de ponto, formas curtas de comandos e macros incoerentes. Se você é um desenvolvedor novo, você apenas chora e foge porque não faz sentido. Se você é um desenvolvedor sênior, você lê e entende um pouco, então decide “isso é nojento; vou fazer melhor de outra forma” – mas então a próxima pessoa a trabalhar na aplicação precisa aprender o paradigma original e o seu novo paradigma inteligente. Estenda isso por 20 anos e você terá um verdadeiro pesadelo. Mas se você é realmente um artista especialista, você pode fazer restauração de arte – ver a elegância na estrutura original e trabalhar dentro dela, consertando gentilmente as coisas arquitetonicamente inconsequentes que fazem o novo desenvolvedor chorar e o desenvolvedor sênior apertar "delete", sem criar uma bagunça fragmentada ou levar anos de trabalho repintando a coisa toda. Talvez você até produza seu próprio trabalho no estilo do velho mestre. A lição mais importante desta história é que, como um artista de verdade, você tem um interesse genuíno em produzir um trabalho de tal qualidade que alguém que o herde sem seus anos de experiência não decida jogar tudo fora – e as “pequenas coisas” como estilo de código, legibilidade e endereçamento de dívida técnica ajudam muito, preservando a arquitetura, se não cada linha de código

Em resumo:

  • Regras e convenções permitem treinar novos artistas brilhantes antes que eles sejam brilhantes.
  • Regras e convenções dão a você uma tela fixa para exercitar a criatividade sem precisar perder tempo tentando se dar bem com as pessoas.
  • Regras e convenções permitem que você faça algo bonito que não será jogado fora pela primeira pessoa a herdá-lo.
Discussão (0)1
Entre ou crie uma conta para continuar
Artigo
· Ago. 15 3min de leitura

A beginner's guide to creating SQL tables and seeing them as classes

The August Article Bounty on the Global Masters article caught my attention, and one of the proposed topics sounded quite interesting in regard to its future use in my teaching. So, here's what I'd like to tell my students about tables in IRIS and how they correlate with the object model. 

First of all, InterSystems IRIS boasts a unified data model. This means that when you work with data, you are not locked into a single paradigm. The same data can be accessed and manipulated as a traditional SQL table, as a native object, or even as a multidimensional array (a global). It means that when you create an SQL table, IRIS automatically creates a corresponding object class. When you define an object class, IRIS automatically makes it available as an SQL table. The data itself is stored only once in IRIS's efficient multidimensional storage engine. The SQL engine and the object engine are simply different "lenses" to view and work with the same data.

First, let's look at the correlation between the relational model and the object model:

Relational Object
Table Class
Column Property
Row Object
Primary key Object Identifier

It's not always a 1:1 correlation, as you may have several tables represent one class, for example. But it's a general rule of thumb. 

In this article, I will discuss creating a table by listing its columns. 

The most basic approach:

CREATE TABLE [IF NOT EXISTS] table (
   column1 type1 [NOT NULL], 
   column2 type2 [UNIQUE], 
   column3 type3 [PRIMARY KEY]
   ...
   [CONSTRAINT fKeyName FOREIGN KEY (column) REFERENCES refTable (refColumn)]
)

[ ] designate the optional parts.

Let's create a table DC.PostType, which consists of three columns: TypeID(primary key), Name, and Description:

CREATE TABLE DC.PostType (
  TypeID        INT NOT NULL,
  Name          VARCHAR(20), 
  Description   VARCHAR(500),
  CONSTRAINT Type_PK PRIMARY KEY (TypeID)
)

As a result, we will get the following class after executing the SQL statement above:

/// 
Class DC.PostType Extends %Persistent [ ClassType = persistent, DdlAllowed, Final, Owner = {UnknownUser}, ProcedureBlock, SqlRowIdPrivate, SqlTableName = PostType ]
{

Property TypeID As %Library.Integer(MAXVAL = 2147483647, MINVAL = -2147483648) [ Required, SqlColumnNumber = 2 ];
Property Name As %Library.String(MAXLEN = 20) [ SqlColumnNumber = 3 ];
Property Description As %Library.String(MAXLEN = 500) [ SqlColumnNumber = 4 ];
Parameter USEEXTENTSET = 1;
/// Bitmap Extent Index auto-generated by DDL CREATE TABLE statement.  Do not edit the SqlName of this index.
Index DDLBEIndex [ Extent, SqlName = "%%DDLBEIndex", Type = bitmap ];
/// DDL Primary Key Specification
Index TypePK On TypeID [ PrimaryKey, SqlName = Type_PK, Type = index, Unique ];
Storage Default
{
<Data name="PostTypeDefaultData">
<Value name="1">
<Value>TypeID</Value>
</Value>
<Value name="2">
<Value>Name</Value>
</Value>
<Value name="3">
<Value>Description</Value>
</Value>
</Data>
<DataLocation>^B3xx.DXwO.1</DataLocation>
<DefaultData>PostTypeDefaultData</DefaultData>
<ExtentLocation>^B3xx.DXwO</ExtentLocation>
<IdFunction>sequence</IdFunction>
<IdLocation>^B3xx.DXwO.1</IdLocation>
<Index name="DDLBEIndex">
<Location>^B3xx.DXwO.2</Location>
</Index>
<Index name="IDKEY">
<Location>^B3xx.DXwO.1</Location>
</Index>
<Index name="TypePK">
<Location>^B3xx.DXwO.3</Location>
</Index>
<IndexLocation>^B3xx.DXwO.I</IndexLocation>
<StreamLocation>^B3xx.DXwO.S</StreamLocation>
<Type>%Storage.Persistent</Type>
}

}

Key Observations:

  • TABLE DC.PostType became Class DC.PostType.
  • The class Extends %Persistent, which is what tells IRIS to store its data in the database.
  • VARCHAR became %String.
  • INT became %Integer.
  • The PRIMARY KEY constraint created an Index with the PrimaryKey keyword.

You can now use this table/class from either side, for example, using SQL:

INSERT INTO DC.PostType (TypeID, Name, Description) VALUES (1, 'Question', 'Ask a question from the Community')

There's a lot more to creating tables using SQL, please read the documentation provided below.

7 Comments
Discussão (7)3
Entre ou crie uma conta para continuar