Adding an Array from one JSON Object to another with UDO

January 12, 2023

Starting with UniVerse 11.1 and UniData 7.3, U2 Dynamic Objects (UDO) provides you with extended basic functions to create and consume JSON and/or XML documents. One question I’ve been asked numerous times is “how do I manage Arrays in UDO objects, and how do I combine properties from multiple objects.”

Before I discuss UDO Array manipulation and adding properties from one object to another, I’ll start with the basics.

In UDO the valid types are:

  • Binary (UDO_FALSE and UDO_TRUE)
  • Null (UDO_NULL)
  • Numeric (UDO_NUMBER)
  • String (UDO_STRING)
  • Array (UDO_ARRAY)
  • Object (UDO_OBJECT)

Here are the steps you’ll follow to create a simple JSON object that contains all of the above types:

Step 1: The UDO.H include file contains the parameter used in the UDO API

(for UniData the include file is INCLUDE, and for UniVerse it is UNIVERSE.INCLUDE)

*
$INCLUDE INCLUDE UDO.H
*

Step 2: Create the initial OBJECT

ST = UDOCreate(UDO_OBJECT, udoHandle); GOSUB TEST_AND_DISPLAY

A newly created UDO Object looks like:

{ }

Please note you cannot directly display the internal structure for a:

  • UDO_OBJECT
  • UDO_ARRAY
  • UDO_TRUE
  • UDO_FALSE or
  • UDO_NULL

or you will get the following runtime error message:

In C:\U2\ud82\sys\CTLG\c\CREATE_SIMPLE_JSON at line 9 Illegal use of the file, select, cursor, BCI, Socket, HTTP, XML, UDO, SCTX ,MQS, SOAP, Python or database variable.

Side note: I will be using the internal subroutine TEST_AND_DISPLAY. It will display the string representation of the UDO_OBJECT ‘udoHandle’. I am adding this so we can see how the object changes throughout the program.

TEST_AND_DISPLAY:

IF ST = UDO_SUCCESS THEN
ST = UDOWrite(udoHandle, UDOFORMAT_JSON, outputString)
CRT outputString

END ELSE

S = UDOGetLastError(errorCode, errorMessage)
CRT "There was an issue: "
CRT errorCode, errorMessage

END
RETURN

Step 3: You can add simple types as name value pairs to the JSON Object

name = "myString" 
value = "This is a string" 
ST = UDOSetProperty(udoHandle, name, value); GOSUB TEST_AND_DISPLAY 
name = "myNumber" 
value = 1.25 
ST = UDOSetProperty(udoHandle, name, value); GOSUB TEST_AND_DISPLAY 

After adding the two properties the UDO Object now looks like: 

{
"myString":     "This is a string", 
"myNumber":     1.250000 

Step 4: Adding binary types and null Elements

Note that you need to create the binary object first, then add to the UDO Object

name = "myTrue"
ST = UDOCreate(UDO_TRUE, value);
ST = UDOSetProperty(udoHandle, name, value); GOSUB TEST_AND_DISPLAY
*

name = "myFalse" 
ST = UDOCreate(UDO_FALSE, value)
ST = UDOSetProperty(udoHandle, name, value); GOSUB TEST_AND_DISPLAY
*

name = "myNull"
ST = UDOCreate(UDO_NULL, value);
ST = UDOSetProperty(udoHandle, name, value); GOSUB TEST_AND_DISPLAY

After you add the above properties, the UDO Object now looks like:


"myString":     "This is a string",
"myNumber":     1.250000,
"myTrue":       true,
"myFalse":      false,
"myNull":       null
}

Step 5. Creating and working with Arrays

Now that you know how to add the simple types, let’s get into something a little bit more complicated.

Working With Arrays:

When you create a new Array object, it is considered stand-alone. This is different than working with an Array that is part of another UDO object. For simplicity purposes, we will create a simple Array, and use several of the UDO Functions related to Arrays.

Step 5.1 Create the empty Array

ST = UDOCreate(UDO_ARRAY, theArray)

Step 5.2 Append items into the array

We can add items to the Array with either of the following two functions:

UDOArrayAppendItem(udoHandle, value)
UDOArrayInsertItem(udoHandle, index, value)

Appending items puts the next item at the end of the Array:

value = "A Rock"
ST = UDOArrayAppendItem(theArray, value)
value = "And a Hard Place"
ST = UDOArrayAppendItem(theArray, value)

After appending the two items the Array looks like:

["A Rock", "And a Hard Place"]

Step 5.3 Insert an item between the two Array elements

index = 2
value = "yourself"
ST = UDOArrayInsertItem(theArray, index, value)

Note that at any time, we can attach the array to a property of our original UDO Object.

Step 5.4 Add the Array to our original UDO object.

name = "myArray"
ST = UDOSetProperty(udoHandle, name, theArray); GOSUB TEST_AND_DISPLAY

After adding the Array, the UDO Object now looks like:

{
"myString": "This is a string",
"myNumber": 1.250000,
"myTrue": true,
"myFalse": false,
"myNull": null,
"myArray": ["A Rock", "yourself", "And a Hard Place"]
}

Step 6: Removing/Changing elements in theArray after setting it to a UOD Object Property

Since we added the stand-alone reference for the Array to the UDO Object, we can continue to work with the Array handle ‘theArray’, and changes will be applied to the original UDO object we added it to.

First let’s remove yourself from the array.

index = 2
ST = UDOArrayDeleteItem(theArray, index)

Then change the 2nd Element

index = 2
value = "beats scissors"
ST = UDOArraySetItem(theArray, index, value)

When we write out our final UDO Object to a JSON string it looks like:

{
"myString": "This is a string",
"myNumber": 1.250000,
"myTrue": true,
"myFalse": false,
"myNull": null,
"myArray": ["A Rock", "beats scissors"]

Step 7 Moving an Array from one UDO_OBJECT to another

If we get an Array element from another UDO object, it is not considered stand-alone. It is part of the original UDO_OBJECT, and cannot be added to the new object until we clone it ( make a copy that is stand-alone)

Step 7.1 Instantiating a new UDO Object from a JSON String.

To show the issue with non-stand-alone object handles, we will need to create a new UOD object from the JSON string:

{ "thisArray": [ 1, 2, 3, 4, 5, 6] }

JSON = ' { "thisArray": [ 1, 2, 3, 4, 5, 6] }'
ST = UDORead(JSON, UDOFORMAT_JSON, newObjectHandle)

Step 7.2 After instantiating the UDO Object, we can get the property

Since there is only one property in the UDO_OBJECT, we can use the UDOGetNextProperty function.

ST = UDOGetNextProperty(newObjectHandle, name, newArray, value_type)

We now have the 'thisArray' property in our newObjectHandle, and since it is tied to that object it is not stand-alone, and we cannot directly add it to our original UDO_OBJECT.

Step 7.3 Trying to add a non-stand-alone object

name = "newArray"
ST = UDOSetProperty(udoHandle, name, newArray); GOSUB
TEST_AND_DISPLAY

After running the above, you will see that ST is set to -1, which indicates an error.

Then, with the output from the UDOGetLastError function, you find that the error is that the object is not stand-alone.

S = UDOGetLastError(errorCode, errorMessage)
CRT errorCode, errorMessage

3 Not a stand-alone UDO object/array

Step 7.4 Cloning the handle to the Array so it can be added to the original UDO_OBJECT

ST = UDOClone(newArray, newArrayHandle)
ST = UDOSetProperty(udoHandle, name, newArrayHandle); GOSUB
TEST_AND_DISPLAY

The udoHandle now contains the new property from the other UDO object.

{
"myString":     "This is a string",
"myNumber":     1.250000,
"myTrue":       true,
"myFalse":      false,
"myNull":       null,
"myArray":      ["A Rock", "beats scissors"],
"newArray":     [1, 2, 3, 4, 5, 6]

No matter if you create your own JSON object from scratch, or start from an existing JSON ( or XML ) document, you now know how to manipulate the Array elements, and move properties from one UDO Object to another.