Openedge Abl Use Json
Openedge Abl Use Json
Openedge Abl Use Json
Copyright
© 2020 Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved.
®
These materials and all Progress software products are copyrighted and all rights are reserved by Progress
Software Corporation. The information in these materials is subject to change without notice, and Progress
Software Corporation assumes no responsibility for any errors that may appear therein. The references in
these materials to specific platforms supported are subject to change.
Corticon, DataDirect (and design), DataDirect Cloud, DataDirect Connect, DataDirect Connect64, DataDirect
XML Converters, DataDirect XQuery, DataRPM, Defrag This, Deliver More Than Expected, Icenium, Ipswitch,
iMacros, Kendo UI, Kinvey, MessageWay, MOVEit, NativeChat, NativeScript, OpenEdge, Powered by Progress,
Progress, Progress Software Developers Network, SequeLink, Sitefinity (and Design), Sitefinity, SpeedScript,
Stylus Studio, TeamPulse, Telerik, Telerik (and Design), Test Studio, WebSpeed, WhatsConfigured,
WhatsConnected, WhatsUp, and WS_FTP are registered trademarks of Progress Software Corporation or one
of its affiliates or subsidiaries in the U.S. and/or other countries. Analytics360, AppServer, BusinessEdge,
DataDirect Autonomous REST Connector, DataDirect Spy, SupportLink, DevCraft, Fiddler, iMail, JustAssembly,
JustDecompile, JustMock, NativeScript Sidekick, OpenAccess, ProDataSet, Progress Results, Progress
Software, ProVision, PSE Pro, SmartBrowser, SmartComponent, SmartDataBrowser, SmartDataObjects,
SmartDataView, SmartDialog, SmartFolder, SmartFrame, SmartObjects, SmartPanel, SmartQuery, SmartViewer,
SmartWindow, and WebClient are trademarks or service marks of Progress Software Corporation and/or its
subsidiaries or affiliates in the U.S. and other countries. Java is a registered trademark of Oracle and/or its
affiliates. Any other marks contained herein may be trademarks of their respective owners.
March 2020
Updated: 2020/09/10
Table of Contents
Index..............................................................................................................49
Purpose
This content provides a programming best practices for OpenEdge developers who want to use JavaScript
Object Notation (JSON) with their ABL (Advanced Business Language) applications.
Audience
This content is intended for ABL programmers working with JSON data in their ABL applications.
Organization
Develop with JSON in OpenEdge on page 9
This section provides an introduction to JSON and an overview of ABL support for serializing ABL data objects
to and from JSON.
Use JSON Objects and Arrays on page 17
This section discusses the ABL features that enable you to serialize ABL data objects to and from JSON. It
covers data type mapping between ABL and JSON, writing ABL data objects to JSON data, reading JSON
data into ABL data objects, and inferring ABL data object schema from JSON data.
Use JSON with ProDataSets, Temp-tables, and Temp-table Buffers on page 23
This section discusses the ABL features that enable you to parse and serialize JSON objects and arrays.
Documentation Conventions
See Documentation Conventions for an explanation of the terminology, format, and typographical conventions
used throughout the OpenEdge content.
Purpose
This content provides a programming best practices for OpenEdge developers who want to use JavaScript
Object Notation (JSON) with their ABL (Advanced Business Language) applications.
Audience
This content is intended for ABL programmers working with JSON data in their ABL applications.
Organization
Develop with JSON in OpenEdge on page 9
This section provides an introduction to JSON and an overview of ABL support for serializing ABL data objects
to and from JSON.
Use JSON Objects and Arrays on page 17
This section discusses the ABL features that enable you to serialize ABL data objects to and from JSON. It
covers data type mapping between ABL and JSON, writing ABL data objects to JSON data, reading JSON
data into ABL data objects, and inferring ABL data object schema from JSON data.
Use JSON with ProDataSets, Temp-tables, and Temp-table Buffers on page 23
This section discusses the ABL features that enable you to parse and serialize JSON objects and arrays.
Documentation Conventions
See Documentation Conventions for an explanation of the terminology, format, and typographical conventions
used throughout the OpenEdge content.
JSON developers use JSON as an alternative data interchange format to XML. XML is widely used to exchange
data in a heterogeneous environment. However, some developers consider XML as too verbose for exchanges
between a web browser and a web server as part of a rich internet application. For more information about
XML, see Use XML with ABL Applications.
JSON's appeal as an alternative to XML comes from smaller HTTP messages and less complicated syntax.
The smaller messages and simpler syntax can provide a significant performance advantage over XML. As a
result, JSON is popular for rich service based applications.
• About JSON
• JSON basics
About JSON
JavaScript Object Notation (JSON) is a data interchange format created from a subset of JavaScript. The
Internet Engineering Task Force's RFC 4627 describes JSON as follows:
"JavaScript Object Notation (JSON) is a lightweight, text-based, language-independent data interchange format.
It was derived from the ECMAScript Programming Language Standard. JSON defines a small set of formatting
rules for the portable representation of structured data."
Benefits of JSON
JSON offers the same kind of benefits that XML does for exchanging data in a heterogeneous environment,
such as the following:
• JSON is self-describing. The syntax and hierarchical structure of the JSON strings can in some cases be
interpreted by applications that do not already know what data to expect.
• JSON is simple text. This fact makes it suitable and safe for transferring across platforms and operating
systems that do not readily share more complex document types. As text, JSON can also be readily displayed
and edited in simple editors.
• JSON is compact. An average JSON string is about two thirds of the size of the same data in XML.
• JSON is easy to learn, easy to read, and easy to understand.
JSON basics
This section provides a brief overview of JSON with the following topics:
The data type of a value is determined by the format of the value. In addition to these primitive data types,
there are some non-standard data types in common usage for certain values. For more information about ABL
support of non-standard data types, see Data type mapping on page 25.
JSON also supports two complex data types, as shown in the following table:
Object A comma-delimited list of { "myString" : "jump rope", "myNum" : 17, "myBool" : false
named values, either simple or }
complex, enclosed in braces
Simple values
A simple value is a name/value pair. The name is always quoted and separated from the value by a colon, as
shown:
"name" : value
The exception to this is the list of values in an array, as shown in the above table. In an array, you access a
particular value by a numeric index, rather than by a name.
Complex values
JSON enables you to name objects and arrays and combine them into complex values. A complex value
combines simple values to represent more complicated data. The following example shows an object made
up of other objects and arrays:
{
"salesRep" : { "name" : "Dorothy Gale",
"age" : 38,
"region" : "Kansas, USA"
},
"tractorSales" : { "2009Quarterly" : [ 13, 27, 18, 9 ],
"2008Quarterly" : [ 11, 17, 32, 5 ],
"2007Quarterly" : [ 9, 25, 16, 10 ]
}
}
Note: The previous JSON example has been formatted for easier reading.
Features
The ABL JSON object model allows you to:
• Parse JSON strings or read ABL data objects (such as temp-tables and ProDataSets) into a hierarchy of
JSON object and JSON array classes
• Update the individual elements (properties) of each object or array in the hierarchy
• Serialize the entire JSON object hierarchy to a variety of JSON string types (memory-resident character
strings, files, etc.)
Use cases
The JSON object model provides the flexibility to interact with JSON in a rich internet application (RIA) that
you might develop using a services back end. Frameworks such as AJAX contain JavaScript object libraries
with full JSON support. A typical use of the API might be as follows:
1. The service receives a request containing JSON.
2. The JSON is parsed to create a tree of JsonObjects and JsonArrays.
3. Based on the content of the generated objects, the application responds to the request, and gathers results.
4. These results are used to modify the existing tree or create a new tree of JSON objects.
5. The application serializes the resulting tree and sends the serialized JSON back as a response.
6. The application deletes the tree(s) of JSON objects.
ABL support
ABL provides various built-in classes that support the JavaScript object model:
Progress.Json.ObjectModel.ObjectModelParser class
This class provides public methods that parse a JSON string from a variety of sources and builds a corresponding
JSON object hierarchy.
Progress.Json.ObjectModel.JsonObject class
This class encapsulates a single JSON object, which can contain any number of properties(name/value pairs)
of various JSON data types, including other JSON objects or arrays. It provides public methods to access and
change the object's data in a number of ways.
Progress.Json.ObjectModel.JsonArray class
This class encapsulates a single JSON array, which can contain any number of elements of various JSON
data types, including other JSON objects or arrays. It provides public methods to access and change the array's
data in a number of ways.
Progress.Json.ObjectModel.JsonDataType
This class provides public properties that allow you to identify the JSON data type of a given property or element
in a JSON object or array.
For more information on classes supporting JavaScript object model, see, Use JSON Objects and Arrays on
page 17
Features
The JSON features of temp-tables and ProDataSets allow you to take advantage of their rich relational features
while providing a standards-based method for sharing data with other application. These JSON features include
the following:
• Temp-table objects
• Temp-table buffer objects (can act on the entire temp-table or the buffers' current contents)
• ProDataSet objects
• JsonObject or JsonArray objects
Use cases
The JSON read and write features are robust and versatile. The following examples demonstrate common use
cases that can be solved with the features:
ABL support
The serialization features are provided by the following methods on buffers, temp-tables, and ProDataSets:
• JsonObject:Read( ) method — Reads the data from a ProDataSet, Temp-table or Temp-table buffer
object to generate a JSON Object.
• JsonArray:Read( ) method — Reads the data from Temp-Table object to generate a JSON Array.
You do not need to be familiar with JSON to use these methods and the associated attributes.
The following figure shows the inheritance relationship between the parser classes:
The following figure shows the inheritance relationship between the classes in the Progress.Json.ObjectModel
package:
JsonObject
The ABL JsonObject is a map collection. It maps string keys to values. Each key/value pair is called a property.
The object's properties are not listed in any specific order. The only way to reference a property is by name.
Because property names are Unicode strings, there is no limitation on the nomenclature of the string. For
instance, whitespace characters may also be included in a property name.
JsonArray
The ABL JsonArray is an array collection. It maps integer keys to values. Each key/value pair is called an
element, and the only way to access an array element's value is by index in the array. There is a strict order
to an array's elements. A JsonArray can grow and shrink in size. Elements can be inserted into and removed
from the middle of a JavaScript array.
JavaScript values are loosely typed. That means, the data type of a value within a JavaScript object or array
can be changed by setting a new value.
When a property or element is set to null, it loses its data type. In a sense null is a data type (or lack of data
type) as well as a value (or lack of value).
Unlike ABL Arrays, JsonArrays can be heterogeneous, meaning that the elements within the array can be of
different data types.
The following is an example of building a heterogeneous JsonArray:
The first three elements are numbers, the fourth element is a boolean, and the fifth element is a string.
The following is the result of myLongchar:
The ParseFile( ) method from the ObjectModelParser class parses the JSON from the specified file. The
value the method returns is either a Progress.Json.ObjectModel.JsonObject or a
Progress.Json.ObjectModel.JsonArray.
The following example shows the reuse of a parser:
In the example above, the AVM parses a LONGCHAR to initialize some settings for the procedure, and then
reads and parses from the WebSpeed input stream. The JSON parser returns JsonObject or JsonArray as a
JsonConstruct which is why CAST() is required.
Class Description
Progress.Json.JsonError
A JsonError is raised when a failure specific to the Progress.Json family of packages
occurs.
Progress.Json.JsonParserError A JsonParserError is raised when the syntax of the JSON being parsed is incorrect.
For more information, see the descriptions of these classes in ABL Reference.
The AVM can serialize the data from ABL temp-tables and ProDataSets to JSON data. Similarly, you can read
or read and load JSON data into a temp-table or ProDataSet.
• Serializing to and from JSON with methods on ProDataSet, temp-table, and temp-table buffer object handles
• Serializing before-image data for ProDataSets
• Serializing fields with data types based on ABL class definitions (see the WRITE-JSON( ) entry in ABL
Reference for more details and restrictions)
Note: ABL does not support serializing schema to and from JSON
Method Description
READ-JSON( ) Reads a specified JSON string, JsonArray object, or JsonObject object into a
corresponding ProDataSet, a temp-table, or a temp-table buffer object.
Buffer-field
SERIALIZE-HIDDEN LOGICAL Indicates whether this field is written when the
(Readable and temp-table is serialized, for example into JSON
writeable) or XML. This attribute als interacts with the
XML-NODE-TYPE attribute.
ProDataSet
SERIALIZE-NAME CHARACTER Optionally specifies the name of a ProDataSet, a
temp-table
(Readable and temp-table, a temp-table buffer, or a temp-table
writeable) temp-table buffer
buffer-field object as it should appear when
temp-table buffer
serialized, for example into JSON or XML. This
field
attribute also interacts with the XML-NODE-NAME
attribute.
Note: The SERIALIZE-HIDDEN and SERIALIZE-NAME attributes provide generalized support for serializing
data objects to either JSON or XML. These attributes interact with the XML-specific attributes in ABL. For more
information, see the descriptions of these attributes in ABL Reference.
Binary data A JSON string consisting of the Base64 encoded equivalent of the binary
data
1
See Minimize the size of JSON data on page 35 for more information.
For serializing ABL data objects to JSON strings, only the ABL data types that you can assign to a temp-table
field are mapped to JSON data types. The exception to this is the ABL CLASS data type. The ABL serialization
features do no support serializing fields with data types based on ABL class definitions.
The following table shows how the AVM maps ABL data types to JSON data types. We only support JsonObject
and JsonArray as types.These mappings apply when the AVM writes ABL data to JSON and when the AVM
reads JSON data into an ABL data object with a defined schema. Class-based objects cannot be added to a
JsonObject or JsonArray.
CHARACTER string
CLOB string
COM-HANDLE number
DECIMAL number
HANDLE number
INT64 number
INTEGER number
RECID number
Note:
When parsing values to or from JSON, the AVM equates the ABL Unknown value (?) to a JSON null value.
If a JSON string's value is too long for the ABL data type, the AVM generates an error message and the
READ-JSON( ) method returns FALSE.
If a JSON number's value is out of the ABL data type's range, the AVM generates an error message and the
READ-JSON( ) method returns FALSE.
If you read JSON data into an ABL data object that has a schema, the AVM assumes that the JSON values
are formatted appropriately for the data object's schema. For example, if the AVM reads JSON data into an
ABL temp-table field of the DATETIME data type, the AVM assumes that the value is a string in the ISO 8601
format. If the JSON value cannot be converted to the expected ABL data type, READ-JSON( ) generates an
error message and returns FALSE.
If you read a JSON string into a dynamic ABL data object that does not have a schema, the AVM infers the
schema from the JSON data. When you read JSON into an ABL data object with an inferred schema, the AVM
uses different rules to assign ABL data types to JSON values. With the inferred data types, the AVM makes
no attempt to determine if the JSON values represent one of the non-standard data types. For example, all
quoted values are mapped to the CHARACTER data type. For more information about inferring schema, see
Infer ABL schemas from JSON data on page 44.
Syntax
WRITE-JSON ( target-type ,
{ file | stream| stream-handle | memptr | longchar
| JsonArray | JsonObject }
[ , formatted [ , encoding [ , omit-initial-values [ , omit-outer-object
[ , write-before-image ] ] ] ] ] )
target-type
A CHARACTER expression that specifies the target type for the JSON output. Valid values are "FILE",
"STREAM", "STREAM-HANDLE", "MEMPTR", "LONGCHAR", "JsonArray", and "JsonObject".
All values except "JsonArray" and "JsonObject" specify a target for a JSON string.
file
A CHARACTER expression that specifies the name of a file to which the AVM writes the JSON string.
You can specify an absolute pathname or a pathname relative to the current working directory. If a
file with the specified name already exists, the AVM verifies that the file is writeable and overwrites
the file.
stream
A CHARACTER expression that specifies the name of a stream. If you specify the empty string (""),
the AVM writes the JSON string to the default unnamed output stream. For WebSpeed, write the
JSON string to the WebSpeed-defined output stream (WEBSTREAM).
stream-handle
memptr
A MEMPTR variable to contain the JSON string in memory. If you do not specify the encoding
parameter, the AVM encodes the text written to the MEMPTR as "UTF-8". This method allocates the
required amount of memory for the JSON string and sets the size of the variable. When you are
finished using the MEMPTR, you must free the associated memory, by setting the MEMTER to zero
bytes with the SET-SIZE statement.
longchar
The AVM saves the JSON string to the LONGCHAR variable in the code page that corresponds to
the character encoding you specify in the encoding option. If you do not specify a character
encoding for the JSON string, the AVM saves the LONGCHAR variable in "UTF-8".
If the LONGCHAR variable's code page is fixed (that is, set using the FIX-CODEPAGE statement) and
the fixed code page is not equivalent to the character encoding you specify in the encoding option,
the WRITE-JSON( ) method generates an error and returns FALSE. The JSON string is not saved
to the LONGCHAR.
JsonArray
JsonObject
formatted
An optional LOGICAL expression where TRUE directs the AVM to format the JSON string in a
hierarchical manner using extra white space, carriage returns, and line feeds. The default value is
FALSE. If you specify the Unknown value (?), the method uses the default value of FALSE. The
formatted option is ignored for JsonArray and JsonObject targets.
encoding
An optional CHARACTER expression that specifies the name of the character encoding the AVM uses
to write the JSON output. The default encoding is "UTF-8".
The encoding name must specify a Unicode transformation format. Valid values are "UTF-8",
"UTF-16", "UTF-16BE", "UTF-16LE", "UTF-32", "UTF-32BE", and "UTF-32LE".
Note: If you specify the empty string ("") or the Unknown value (?), the AVM uses the default
encoding of "UTF-8".
omit-initial-values
An optional LOGICAL expression where TRUE directs the AVM to exclude temp-table fields containing
their initial values from the JSON output, and FALSE directs the AVM to include all temp-table field
data in the output. The default value is FALSE. If you specify the Unknown value (?), the method
uses the default value of FALSE.
For more information about this option, see Minimize the size of JSON data on page 35 .
omit-outer-object
An optional LOGICAL expression that indicates whether the outer-most object is included in the
JSON output. TRUE directs the AVM to remove the outer-most object on output. FALSE directs the
AVM to include the outer-most object in the output.
For more information about this option, see Omit the outer object in JSON data on page 35.
write-before-image
An optional LOGICAL expression where TRUE directs the AVM to include ProDataSet before-image
data and error information in the JSON output. The default value is FALSE.
This element can only be set to TRUE for a ProDataSet. If its value is set to TRUE for a temp-table
or buffer handle, the WRITE-JSON( ) method returns an error and a value of FALSE.
/* write-json-pds1.p */
{pi-json-parameterVarDefs.i} /* parameter variable definitions */
{pi-write-json-pds1.i} /* dsOrderLog definition - no nesting */
ASSIGN
cTargetType = "file"
cFile = "dsOrderLog.json"
lFormatted = TRUE
cEncoding = ?.
{"dsOrderLog": {
"ttCustomer": [
{
"CustNum": 2,
...
}
],
"ttOrder": [
{
"Ordernum": 94,
...
},
...
{
"Ordernum": 6070,
...
}
],
"ttInvoice": [
{
"Invoicenum": 94,
...
},
{
"Invoicenum": 124,
...
}
]
}}
Because the ProDataSet definition did not include the NESTED option for the data-relations, the records from
each temp-table are presented after each other. If you do not nest child tables, the JSON does not contain the
data relation information. The serialization process also loses any information about key columns.
If you run write-json-pds2.p which uses a ProDataSet with nested child tables, the resulting JSON looks
like this:
{"dsOrderLog": {
"ttCustomer": [
{
"CustNum": 2,
...
"EmailAddress": "",
"ttOrder": [
{
"Ordernum": 94,
...
"Carrier": "Standard Mail",
"ttInvoice": [
{
"Invoicenum": 94,
...
}
]
},
{
"Ordernum": 125,
...
"Carrier": "FlyByNight Courier",
"ttInvoice": [
{
"Invoicenum": 124,
...
}
]
},
... ]
}
]
}}
/* write-json-tt.p */
{pi-json-parameterVarDefs.i} /* parameter variable definitions */
{pi-write-json-tt.i} /* ttCust definition */
ASSIGN
cTargetType = "FILE"
cFile = "ttCust.json"
lFormatted = TRUE
cEncoding = ?.
{"ttCust": [
{
"CustNum": 1,
"Country": "USA",
"Name": "Lift Tours",
"Address": "276 North Drive",
"Address2": "",
"City": "Burlington",
"State": "MA",
"PostalCode": "01730",
"Contact": "Gloria Shepley",
"Phone": "(617) 450-0086",
"SalesRep": "HXM",
"CreditLimit": 66700.00,
"Balance": 903.64,
"Terms": "Net30",
"Discount": 35,
"Comments": "This customer is on credit hold.",
"Fax": "",
"EmailAddress": ""
},
{
"CustNum": 2,
... },
{
"CustNum": 3,
... }
]}
/* write-json-pds3.p */
{pi-json-parameterVarDefs.i} /* parameter variable definitions */
{pi-write-json-pds1.i} /* dsOrderLog definition - no nesting */
/* modify ttCustomer 2 */
FIND ttCustomer WHERE ttCustomer.CustNum = 2.
ASSIGN ttCustomer.EmailAddress = "www.progress.com".
/* delete an order */
FIND ttOrder WHERE ttOrder.OrderNum = 94.
DELETE ttOrder.
cEncoding, lOmitInitialValues,
lOmitOuterObject, lWriteBeforeImage).
{"dsOrderLog": {
"prods:hasChanges": true,
"ttCustomer": [
{
"prods:id": "ttCustomer14592",
"prods:rowState": "modified",
"prods:hasErrors": true,
"CustNum": 2,
...
"EmailAddress": "www.progress.com"
}
],
"ttOrder": [
{
"Ordernum": 125,
...
},
...
{
"Ordernum": 6070,
...
],
"ttInvoice": [
{
"Invoicenum": 94,
...
},
{
"Invoicenum": 124,
...
}
],
"prods:before": {
"ttCustomer": [
{
"prods:id": "ttCustomer14592",
"prods:rowState": "modified",
"CustNum": 2,
...
"EmailAddress": ""
}
],
"ttOrder": [
{
"prods:id": "ttOrder12545",
"prods:rowState": "deleted",
"CustNum": 2,
"OrderNum": 94,
...
}
]
},
"prods:errors": {
"ttCustomer": [
{
"prods:id": "ttCustomer14592",
"prods:error": "ttCustomer 2 error"
}
]
}
}}
The prods:before object contains the before-image data for the ProDataSet.
• For ProDataSets with nested data relations, you can choose to omit the duplicate entries of foreign key
fields that appear in each child record nested within a parent record.
• For any ProDataSet or temp-table, you can choose to omit fields from the JSON whose current value is the
same as the initial value of that field.
{"tt": [
{"f1": 11, "f2": 12, "f3": 13},
{"f1": 21, "f2": 22, "f3": 23},
{"f1": 31, "f2": 32, "f3": 33}
]}
[
{"f1": 11, "f2": 12, "f3": 13},
{"f1": 21, "f2": 22, "f3": 23},
{"f1": 31, "f2": 32, "f3": 23}
]
{"pds" : {
"tt1" : [
{"f1": 11, "f2": 12, "f3": 13},
{"f1": 21, "f2": 22, "f3": 23},
{"f1": 31, "f2": 32, "f3": 33}
],
"tt2": [
{"f1": 11, "f2": 12, "f3": 13},
{"f1": 21, "f2": 22, "f3": 23},
{"f1": 31, "f2": 32, "f3": 33}
],
"tt3": [
{"f1": 11, "f2": 12, "f3": 13},
{"f1": 21, "f2": 22, "f3": 23},
{"f1": 31, "f2": 32, "f3": 33}
]
}}
{
"tt1": [
{"f1": 11, "f2": 12, "f3": 13},
{"f1": 21, "f2": 22, "f3": 23},
{"f1": 31, "f2": 32, "f3": 33}
],
"tt2": [
{"f1": 11, "f2": 12, "f3": 13},
{"f1": 21, "f2": 22, "f3": 23},
{"f1": 31, "f2": 32, "f3": 33}
],
"tt3": [
{"f1": 11, "f2": 12, "f3": 13},
{"f1": 21, "f2": 22, "f3": 23},
{"f1": 31, "f2": 32, "f3": 33}
]
}
For the same ProDataSet, the above values are generated when the omit-outer-object is TRUE.
Before using this option, consider carefully whether the consumers of the JSON data will handle the missing
key fields appropriately. In ABL, the READ-JSON( ) method automatically populates foreign keys in nested
child records with the value in the outer parent record when the foreign key is omitted from the JSON data.
Unless you are sure that a non-ABL consumer of the JSON data will do the same, do not use this option in
your nested data-relations.
For example, while a Web browser can read the JSON data and populate a JavaScript object, it will create
rows in the nested table without the foreign key field.
DECIMAL 0
INT64 0
INTEGER 0
LOGICAL No
Omitting initial value fields from your JSON can be useful if:
• Fields with initial values are not important to the business or application logic of your JSON consumer
• The JSON consumer knows how to recreate missing fields and populate them with initial values
To omit these fields, specify TRUE for the optional omit-initial-values argument of the WRITE-JSON( )
method.
When working with large ProDataSets, omitting fields containing their initial values can yield smaller JSON
values, more efficient network transfers, and performance gains with the READ-JSON( ) and WRITE-JSON( )
methods.
Although using the omit-initial-values option can give your application performance and resource use
improvements, you must be sure that the consumers of the generated JSON output can correctly handle it.
The ABL READ-JSON( ) method always populates created records with initial values from the temp-table or
ProDataSet definition. Other applications might not do this.
Syntax
target-format
A CHARACTER expression that specifies the target format used to serialize the current row. Valid
values are "JSON" and "XML". However, this method description focuses on serializing to JSON.
For information on using this method to serialize the current row of a temp-table buffer to XML, see
Use XML with ABL Applications.
target-type
A CHARACTER expression that specifies the target type for the JSON output. Valid values are "FILE",
"STREAM", "STREAM-HANDLE", "MEMPTR", "LONGCHAR", and "JsonObject".
file
A CHARACTER expression that specifies the name of a file to which the AVM writes the JSON string.
You can specify an absolute path or a path relative to the current working directory. If a file with the
specified name already exists, the AVM verifies if the file is writeable and overwrites the file.
stream
A CHARACTER expression that specifies the name of a stream. If you specify the empty string (""),
the AVM writes the JSON string to the default unnamed output stream. For WebSpeed, write the
JSON string to the output stream defined by WebSpeed (WEBSTREAM).
stream-handle
memptr
A MEMPTR variable to contain the JSON string in memory. If you do not specify the encoding
parameter, the AVM encodes the text written to the MEMPTR as "UTF-8". This method allocates the
required amount of memory for the JSON string and sets the size of the variable. When you finish
using the MEMPTR, you must free the associated memory by setting the MEMTER to 0 bytes with the
SET-SIZE statement.
longchar
The AVM saves the JSON string to the LONGCHAR variable in the code page which corresponds to
the character encoding you specify in the encoding option. If you do not specify a character
encoding for the JSON string, the AVM saves the LONGCHAR variable in "UTF-8".
If the LONGCHAR variable's code page is fixed (that is, set using the FIX-CODEPAGE statement) and
the fixed code page is not equivalent to the character encoding you specify in the encoding option,
SERIALIZE-ROW( ) generates an error and returns FALSE. The JSON string is not saved to the
LONGCHAR.
JsonObject
formatted
An optional LOGICAL expression where TRUE directs the AVM to format a JSON string in a hierarchical
manner using white space, carriage returns, and line feeds. The default value is FALSE. If you specify
the Unknown value (?), the method uses the default value FALSE. The formatted option is ignored
for JsonObject targets.
encoding
An optional CHARACTER expression that specifies the name of the character encoding the AVM uses
to write the JSON output. The default encoding is "UTF-8".
The encoding name must specify a Unicode transformation format. Valid values are "UTF-8",
"UTF-16", "UTF-16BE", "UTF-16LE", "UTF-32", "UTF-32BE", and "UTF-32LE".
Note: If you specify the empty string ("") or the Unknown value (?), the AVM uses the default
encoding "UTF-8".
omit-initial-values
An optional LOGICAL expression where TRUE directs the AVM to exclude temp-table fields containing
their initial values from the JSON output, and FALSE directs the AVM to include all temp-table field
data in the output. The default value is FALSE. If you specify the Unknown value (?), the method
uses the default value of FALSE.
For more information about this option, see the Minimize the size of JSON data on page 35.
omit-outer-object
An optional LOGICAL expression indicating whether the outer-most object in JSON output is included.
TRUE directs the AVM to remove the outer-most object on output. FALSE directs the AVM to include
the outer-most object in the output.
For more information about this option, see the Omit the outer object in JSON data on page 35.
The SERIALIZE-ROW( ) method produces the following JSON string outputs. The JSON output below is
bcust.json with omit-outer-object set to FALSE.
{"ttCust":
{
"custNum": 3000,
"Name": "Lift Tours",
"Address": "276 North Drive"
}
}
{
"custNum": 3000,
"Name": "Lift Tours",
"Address": "276 North Drive"
}
Syntax
READ-JSON ( source-type ,
{ file | memptr | handle | longchar | JsonArray | JsonObject }
[ , read-mode ] )
source-type
A CHARACTER expression that specifies the source JSON string type. Valid values are "FILE",
"MEMPTR", "HANDLE", "LONGCHAR", "JsonArray", and "JsonObject".
All values except "JsonArray" and "JsonObject" specify a source for a JSON string.
file
A CHARACTER expression that specifies the name of a file. You can specify an absolute pathname
or one relative to the current working directory. The AVM verifies that the file exists and is accessible.
memptr
A MEMPTR variable that contains the JSON string in memory. The size of the MEMPTR variable must
match the size of the JSON string.
handle
This method reads a JSON string from the WebSpeed Transaction Server. The method verifies that
the JSON string was posted to the WebSpeed Transaction Server by checking that the handle's
IS-JSON attribute is YES. The method also verifies that ABL is running in a WebSpeed environment.
longchar
JsonArray
JsonObject
read-mode
An optional CHARACTER expression that specifies the mode in which this method reads data from
the JSON source into a temp-table or a ProDataSet member buffer. The expression must evaluate
to "APPEND", "EMPTY", "MERGE", or "REPLACE". The default value is "MERGE".
The following table lists the READ-JSON( ) method modes for reading data.
"APPEND" Reads data from the JSON source into the ProDataSet or temp-table object by adding
new records to the existing records, without performing any record comparisons. If a
record from the JSON source exists in the object (that is, it results in a duplicate unique
key conflict), the method generates an error message and returns FALSE.
"EMPTY" Empties the contents of the ProDataSet or temp-table object before reading in data
from the JSON source.
"MERGE" Reads data from the JSON source into the ProDataSet or temp-table object by merging
new records with existing records in the table. If a record from the JSON source exists
in the object (that is, it results in a duplicate unique key conflict), the method does not
replace the existing record. If the record from the JSON source does not exist in the
object, the method creates a new record.
"REPLACE" Reads data from the JSON source into the ProDataSet or temp-table object by merging
new records with existing records in the table. If the record from the JSON source exists
in the object (that is, it results in a duplicate unique key conflict), the method replaces
the existing record with the new record. If the record from the JSON source does not
exist in the object, the method creates a new record.
For a static ProDataSet or temp-table, the serialize name or object name must match the corresponding name
found in the JSON source. If the names do not match, the AVM generates an error message and the method
returns FALSE. The AVM ignores any columns in the JSON source that do not map to temp-table columns. If
you use the SERIALIZE-NAME option in the DEFINE DATASET or DEFINE TEMP-TABLE statement, the AVM
uses that name for matching, rather than the ABL object name.
Note: While reading JSON data into an ABL data object, the AVM does not respond to ProDataSet events.
The AVM also does not track changes to the data in the ProDataSet or temp-table object, meaning that
before-image tables are not updated.
/* read-json-pds2.p */
{pi-json-parameterVarDefs.i} /* parameter variable definitions */
ASSIGN
cSourceType = "file"
cFile = "dsOrderLog2.json"
cReadMode = "EMPTY".
ASSIGN
cTargetType = "file"
cFile = "dsOrderLog3.json"
lFormatted = TRUE
cEncoding = ?.
When you compare the JSON files, you see that only the data that fit into the new ProDataSet made it from
dsOrderLog2.json to dsOrderLog3.json.
Caution: In general, reading JSON into an ABL data object with an inferred schema is less predictable than
the alternative. You should consider carefully how this technique might affect the data, especially if you plan
to return the data after processing it.
When the AVM has to infer schema for the data object, the AVM makes two passes through the JSON data:
one to build the schema and one to fill in the data. On the first pass, the AVM reads all the records before
finalizing the schema, which has the following effects:
• When the AVM parses a JSON null, it provisionally assigns a CHARACTER data type to the column. If a
subsequent record includes a non-null value for that column, the AVM assigns that data type to the column.
In either case, the AVM equates the JSON null value to the Unknown value (?).
• If different rows contain different fields, the final schema includes all the fields.
The AVM infers ABL schema from JSON data using the following guidelines:
• Any JSON object containing an array of objects is a temp-table. The temp-table's name is the array's name.
• The entries in an array of objects are the rows of a single temp-table.
• Each name/value pair in a row's object is a column in the temp-table. The column's name is the JSON
value's name.
• If a value in a row object is a JSON array, it is an array column. The AVM infers the data type of the array
column from the first value in the inner array.
• Any JSON object that is not an array of objects, but that contains at least one object from which the AVM
infers a temp-table, is a ProDataSet. The ProDataSet's name is the JSON object's name.
• If the AVM encounters an array of objects within another array of objects, the AVM infers it to be a nested
temp-table inside the ProDataSet.
• If the AVM infers a temp-table nested within another inferred temp-table, the AVM attempts to create a
relationship between the two tables. If there is only one pair of fields with matching names in the parent and
child tables, the AVM creates a data-relation between the parent table and nested child table using the
matching fields for the pairs-list. If there are no matching fields, the AVM creates a parent-id-relation between
the parent and nested child and adds a RECID field to the child table to maintain the relationship. If there
is more than one pair of matching fields between the tables, the AVM generates an error message and the
READ-JSON( ) method returns FALSE.
Note: If you call READ-JSON( ) on a temp-table object and the AVM infers a nested temp-table, the method
generates an error message and returns FALSE. If you call READ-JSON( ) on a ProDataSet object and the
JSON data contains only a temp-table, the method generates an error message and returns FALSE.
The following table shows how the AVM maps JSON data types to ABL data types when inferring the schema
of a temp-table. By comparing the following table with Table 7, you can see that the differences in the data
type mapping make it unlikely that an inferred temp-table matches the original object from which the data was
read.
null CHARACTER
Note: When inferring ABL data types, the AVM does not try to determine if a JSON value represents one of
the non-standard data types listed in Table 6. The AVM infers a JSON value representing a non-standard data
type as a JSON string and assigns it a CHARACTER data type in ABL. For example, a JSON string value in ISO
8601 format is interpreted as a CHARACTER field, not a DATETIME field.
2
If a JSON string's value is too long for the ABL data type, the AVM generates an error message and the READ-JSON( ) method
returns FALSE.
3
If a JSON number's value is out of the ABL data type's range, the AVM generates an error message and the READ-JSON( )
method returns FALSE.
As an example for the inferring process, take the following JSON object:
{"ttCust":
[
{"Name": ["L", "Frank", "Baum"], "CustNum": 1, "GoldStatus": null},
{"Name": ["Alfred", "E", "Newman"], "CustNum": 2, "GoldStatus": false},
{"Name": ["Bullwinkle", "J", "Moose"], "CustNum": 3, "GoldStatus": true}
]
}
The JSON object contains an array name, "ttCust". The AVM sees that it is an array of objects and creates
a temp-table named ttCust to hold the data. The first name/value pair in the row's object is an array of JSON
string values named "Name". The AVM creates a CHARACTER field, Name, of EXTENT 3 as the temp-table's
first column. The next name/value pair is a JSON number named "CustNum". The AVM creates a DECIMAL
field, CustNum, as the second column.The final name/value pair contains a JSON null named "GoldStatus".
Because the value is a null, the AVM temporarily chooses CHARACTER as the final column's data type. The
AVM then reads the next record and determines that it contains a JSON boolean for the final pair and creates
a LOGICAL field, GoldStatus, as the final column.
The following procedure reads the output from write-json-pds2.p into a dynamic ProDataSet, inferring
the ProDataSet's schema from the JSON data. It then outputs the schema and data to another file, so you can
examine the results:
/* read-json-infer-pds2.p */
DEFINE VARIABLE hDataset AS HANDLE NO-UNDO.
DEFINE VARIABLE lRetOK AS LOGICAL NO-UNDO.
DEFINE VARIABLE hQuery AS HANDLE NO-UNDO.
DEFINE VARIABLE hBuffer AS HANDLE NO-UNDO.
DEFINE VARIABLE hField AS HANDLE NO-UNDO.
DEFINE VARIABLE idx1 AS INTEGER NO-UNDO.
DEFINE VARIABLE idx2 AS INTEGER NO-UNDO.
DEFINE VARIABLE idx3 AS INTEGER NO-UNDO.
PROCEDURE displayResults:
MESSAGE "READ-JSON return value: " lRetOK SKIP.
MESSAGE SKIP "** hDataset schema info **" SKIP.
MESSAGE "ProDataSet name: " hDataset:NAME
"Num-buffers " hDataset:NUM-BUFFERS.
DO idx1 = 1 TO hDataset:NUM-BUFFERS:
hBuffer = hDataset:GET-BUFFER-HANDLE(idx1).
MESSAGE SKIP "Buffer " idx1 "Buffer name: " hBuffer:NAME.
MESSAGE "Buffer Field info".
DO idx2 = 1 TO hBuffer:NUM-FIELDS:
hField = hBuffer:BUFFER-FIELD(idx2).
MESSAGE "Field name: " hField:NAME "Data type: " hField:DATA-TYPE
" Extent: " hField:EXTENT.
END. /* idx2 loop */
END. /* idx1 loop */
MESSAGE SKIP "** hDataset data **".
DO idx1 = 1 TO hDataset:NUM-BUFFERS:
hBuffer = hDataset:GET-BUFFER-HANDLE(idx1).
MESSAGE "*** Buffer " hBuffer:NAME " Data: ***".
CREATE QUERY hQuery.
hQuery:SET-BUFFERS(hBuffer).
The output from write-json-pds2.p is designed with the READ-JSON( ) method's inferring feature in
mind. The ProDataSet's tables have only one possible foreign key between each pair of nested tables. If you
ran write-json-pds2.p substituting the following ProDataSet definition for the include file and then ran
read-json-infer-pds2.p on the output, the procedure would generate several errors because there are
several fields in each table that match fields in the outer tables.
Index
A JSON (continued)
minimizing output 36
ABL support non-standard data types 25
data type mapping 25 primitive data types 10
non-standard JSON data types 25 serialization related attributes 24
AJAX simple values 11
JSON popularity 9 unsupported ABL features 23
Array use in OpenEdge 10
JSON data type 10
N
B
Null
Boolean JSON data type 10
JSON data type 10 Number
JSON data type 10
C
O
Codepages
encoding for serialization 27, 38 Object
Complex data types JSON data type 10
JSON 10
Complex values
JSON structure 12
P
Parsers
D included in OpenEdge 10
Primitive data types
Data types JSON 10
array 10 ProDataSets
Boolean 10 before-image data 27, 33
complex 10 INITIAL option 37
inferred 44 NESTED option 30
mapping 25 omitting foreign keys 36
non-standard JSON 25 reading JSON into 41, 43
null 10 serialization example 30
number 10 serialization related attributes 24
object 10 serializing to JSON 27
primitive 10
string 10
R
J READ-JSON( ) method
data objects with schema 43
JavaScript Object Notation (JSON) inferring schema 44
JSON 9, 17, 20 syntax 41
JSON
ABL support for 23
as XML alternative 9
S
complex data types 10 Schema
complex values 12 inferred data types 44
data type mapping 25 inferring 44
definition 9, 17, 20 READ-JSON( ) method 43
inferring schema 44
T X
Temp-tables XML
INITIAL option 37 serialization attribute interaction 24
reading JSON into 41