Skip to content

JSON Transfer and Parsing

The cJsonHttpTransfer class

This class provides an easy mechanism for sending and receiving JSON documents via HTTP. JSON documents are sent and retrieved as JSON objects.

This class depends on the cJsonObject class. It is expected that this class always uses cJsonObject objects in in- and out-parameters. JSON being sent will be created using the cJsonObject class interface and JSON being returned will be analyzed using the cJsonObject class interface.

This class can be used to transfer data in a RESTful manner. Interfaces exist allowing you to use all of the common HTTP verbs: HttpDeleteJson, HttpGetJson, HttpPatchJson, HttpPostJson, and HttpPutJson. An additional method HttpVerbJson can be used where you can pass any verb of your choosing.

Sample

Here is an example of transferring data as JSON objects using HttpPostJson:

// returns the response JSON data. If 0, no data but legal,
// if -1, bad transfer
Function TransferMyData Handle hoReqJson Returns Handle
    Handle hoHttp
    Handle hoRcvdJson
    Boolean bOk

    // Create JSON HTTP transfer object.
    Get Create (RefClass(cJsonHttpTransfer)) to hoHttp

    // post JSON document and receive returned JSON document
    Get HttpPostJson of hoHttp "dataaccess.com" ;
        "MyApplication/JsonService.asp" hoReqJson (&bOk) to hoRcvdJson

    // transfer object no longer needed
    Send Destroy of hoHttp

    // it’s actually possible for a valid transfer to not return an object
    // if a transfer error, we return -1 (remember this is an example)
    If not (bOk) Begin
        Move -1 to hoRcvdJson
    End

    Function_Return hoRcvdJson // if >0, this is newly created, and must be destroyed
End_Function

The JSON data objects are passed and returned as UTF-8 data over HTTP. This is the expected encoding for JSON transfers over HTTP.

Transfer errors can be further analyzed by using the peJsonTransferStatus property.

This class allows you to transfer and receive data that is larger than the string size limitations imposed by DataFlex. It does this by augmenting OnDataReceived and uses UChar[] arrays to manipulate the data. You may wish to study the class source code to see how this is done so you can create other cHttpTransfer subclasses that allow you to transfer large amounts of data. Note that the event OnDataReceived is not a public event in this class — you should not change it. A similar class, cXmlHttpTransfer, exists for transferring XML documents using XML.


The JSON parser (cJsonObject)

The JSON parser is represented by a single cJsonObject class that contains the entire API for parsing, generating, analyzing and manipulating JSON. The API can be seen as a simplified DOM where each object represents a single JSON node. JSON can be generated by creating and configuring these objects before calling Stringify to generate the JSON string. JSON can be parsed by calling Parse (or ParseUtf8) and then using the API to analyze/enumerate the object structure.

A special feature is the possibility to generate the JSON object structure based on DataFlex struct or array data in memory. Calling DataTypeToJson will generate cJsonObject objects that match the structure of the data and calling Stringify will fully serialize this data into a string. The reverse is done using JsonToDataType, which converts the JSON object structure into struct/array data. This feature requires the JSON to match the format of the struct/array data or errors will be thrown.

Example: Parse known JSON (extensive)

This sample shows how a JSON string passed in as a UChar[] is parsed into a JSON object structure and values are extracted.

Input:

{ "Value1" : "Test data!", "Value2" : 381 }

Output:

Value 1: Test data!
Value 2: 381

Code:

Procedure ParseJSON UChar[] aData
    Handle hoObj hoMem
    Boolean bTest bSuccess
    String sVal
    Integer iVal

    Get Create (RefClass(cJsonObject)) to hoObj
    Get ParseUtf8 of hoObj aData to bSuccess

    If (bSuccess) Begin
        Get IsOfJsonType of hoObj jsonTypeObject to bTest
        If (bTest) Begin
            Get Member of hoObj "Value1" to hoMem
            If (hoMem > 0) Begin
                If (IsOfJsonType(hoMem, jsonTypeString)) Begin
                    Get JSONValue of hoMem to sVal
                    Showln ("Value 1: " + sVal)
                End
                Send Destroy of hoMem
            End

            // Get Value2 member
            Get Member of hoObj "Value2" to hoMem
            If (hoMem > 0) Begin
                If (IsOfJsonType(hoMem, jsonTypeInteger)) Begin
                    Get JSONValue of hoMem to iVal
                    Showln ("Value 2: " + iVal)
                End
                Send Destroy of hoMem
            End
        End
    End

    Send Destroy of hoObj
End_Procedure

Example: Parse known JSON (optimized)

A simplified version using helper functions IsMemberOfJsonType and MemberValue for direct access:

Procedure ParseJSON UChar[] aData
    Handle hoObj
    Boolean bTest bSuccess
    String sVal
    Integer iVal

    Get Create (RefClass(cJsonObject)) to hoObj
    Get ParseUtf8 of hoObj aData to bSuccess

    If (bSuccess and IsOfJsonType(hoObj, jsonTypeObject)) Begin
        If (IsMemberOfJsonType(hoObj, "Value1", jsonTypeString)) Begin
            Get MemberValue of hoObj "Value1" to sVal
            Showln ("Value 1: " + sVal)
        End
        If (IsMemberOfJsonType(hoObj, "Value2", jsonTypeInteger)) Begin
            Get MemberValue of hoObj "Value2" to iVal
            Showln ("Value 2: " + iVal)
        End
    End

    Send Destroy of hoObj
End_Procedure

Example: Generate JSON (long)

This example shows how to create a JSON object structure to generate JSON into a string. It reuses the member object by reinitializing it.

Function GenerateJSON String sVal String sVal2 Integer iVal Returns UChar[]
    Handle hoObj hoMem
    String sResult

    Get Create (RefClass(cJsonObject)) to hoObj
    Send InitializeJsonType of hoObj jsonTypeObject

    Get Create (RefClass(cJsonObject)) to hoMem
    Send InitializeJsonType of hoMem jsonTypeString
    Set JSONValue of hoMem to sVal
    Send SetMember of hoObj "Value1" hoMem

    Set JSONValue of hoMem to sVal2
    Send SetMember of hoObj "Value2" hoMem

    Send InitializeJsonType of hoMem jsonTypeInteger
    Set JSONValue of hoMem to iVal
    Send SetMember of hoObj "Value3" hoMem

    Send Destroy of hoMem
    Get Stringify of hoObj to sResult
    Send Destroy of hoObj
    Function_Return sResult
End_Function

Example: Generate JSON (optimized)

The optimized version, using SetMemberValue:

Function GenerateJSON String sVal Integer iVal Returns String
    Handle hoObj
    String result

    Get Create (RefClass(cJsonObject)) to hoObj
    Send InitializeJsonType of hoObj jsonTypeObject
    Send SetMemberValue of hoObj "Value1" jsonTypeString sVal
    Send SetMemberValue of hoObj "Value2" jsonTypeInteger iVal
    Set peWhitespace of hoObj to jpWhitespace_Spaced
    Get Stringify of hoObj to result
    Send Destroy of hoObj
    Function_Return result
End_Function

Example: Generate JSON Array (long)

Function GenerateJSONArray Integer iLen Returns UChar[]
    Handle hoObj hoMem
    UChar[] result
    Integer iCur

    Get Create (RefClass(cJsonObject)) to hoObj
    Send InitializeJsonType of hoObj jsonTypeArray

    Get Create (RefClass(cJsonObject)) to hoMem
    Send InitializeJsonType of hoMem jsonTypeInteger
    For iCur from 0 to iLen
        Set JSONValue of hoMem to iCur
        Send AddMember of hoObj hoMem
    Loop

    Get StringifyUtf8 of hoObj to result
    Send Destroy of hoMem
    Send Destroy of hoObj
    Function_Return result
End_Function

Example: Generate JSON Array (short)

This could be optimized with an AddMemberValue procedure that can be used to add simple typed members directly:

Function GenerateJSONArray Integer iLen Returns UChar[]
    Handle hoObj
    UChar[] result
    Integer iCur

    Get Create (RefClass(cJsonObject)) to hoObj
    Send InitializeJsonType of hoObj jsonTypeArray
    For iCur from 0 to iLen
        Send AddMemberValue of hoObj jsonTypeInteger iCur
    Loop
    Get StringifyUtf8 of hoObj to result
    Send Destroy of hoObj
    Function_Return result
End_Function

Example: Parse unknown JSON

The following example demonstrates traversing a tree of JSON objects. It handles arrays, objects and other simple types and outputs the structure to the console.

Procedure WriteJSONSimple Handle hoObj
    String sVal
    Get JSONValue of hoObj to sVal
    Showln ("'" + sVal + "'")
End_Procedure

Procedure WriteJSONArray Handle hoObj
    Handle hoMem
    Integer iCur iTo
    Get MemberCount of hoObj to iTo
    For iCur from 0 to (iTo - 1)
        Get MemberByIndex of hoObj iCur to hoMem
        Show ("Member '" + String(iCur) + "': ")
        Send WriteJSON hoMem
    Loop
End_Procedure

Procedure WriteJSONObject Handle hoObj
    Handle hoMem
    Integer iCur iTo
    String sName
    Get MemberCount of hoObj to iTo
    For iCur from 0 to (iTo - 1)
        Get MemberByIndex of hoObj iCur to hoMem
        Get MemberNameByIndex of hoObj iCur to sName
        Show ("Member '" + sName + "': ")
        Send WriteJSON hoMem
    Loop
End_Procedure

Procedure WriteJSON Handle hoObj
    Integer eType
    Get JsonType of hoObj to eType
    Case Begin
        Case (eType = jsonTypeObject)
            Send WriteJSONObject hoObj
            Break
        Case (eType = jsonTypeArray)
            Send WriteJSONArray hoObj
            Break
        Case Else
            Send WriteJSONSimple hoObj
    Case End
End_Procedure

Example: Deserialize JSON into struct

This shows how to deserialize JSON data into a specific struct type:

Function DeserializeFromJson String sData Returns tMyStruct
    tMyStruct tResult
    Handle hoObj
    Boolean bSucces

    Get Create (RefClass(cJsonObject)) to hoObj
    Get Parse of hoObj sData to bSucces
    If (bSucces) Begin
        Get JsonToDataType of hoObj to tResult
    End
    Send Destroy of hoObj
    Function_Return tResult
End_Function

Example: Serialize struct into JSON

This shows how to serialize a struct into JSON:

Function SerializeToJson tSimpleStructTest tInput Returns UChar[]
    UChar[] uData
    Handle hoObj

    Get Create (RefClass(cJsonObject)) to hoObj
    Set peWhitespace of hoObj to jpWhitespace_Pretty
    Set pbUseVariantMetaData of hoObj to True
    Send DataTypeToJSON of hoObj tInput
    Get StringifyUtf8 of hoObj to uData
    Send Destroy of hoObj
    Function_Return uData
End_Function

Example: Manipulate JSON Object

Procedure AlterJSON Handle hoObj
    Send SetMemberValue of hoObj "Value1" jsonTypeString "Changed Value"
End_Procedure

Implementation

The API wraps the JSON-C APIs and provides an object-oriented API for parsing, enumerating, serializing, deserializing and generating JSON. The JSON-C API works by providing pointers to structs that represent JSON objects. Global functions provide operations that can be performed on those objects. Reference counting is used to manage memory; the documentation explains the ownership methodology. Some functions take ownership of an object; where needed ownership must be taken back by incrementing the reference count (using json_object_get). Releasing is done using json_object_put.

Each DataFlex cJsonObject object basically wraps a pointer to a single JSON-C object. A cJsonObject can represent the different types of objects in a JSON structure (object, array, string, boolean, number). The manipulation functions on the cJsonObject class must ensure that they always keep ownership of the underlying JSON-C object. In some cases that means explicitly calling json_object_get. When releasing an object (for example at Destroy) they will call json_object_put.


cJsonObject (API reference)

The cJsonObject represents a single JSON node within a structure of JSON nodes. A JSON node can represent an object, array, simple type (string, number and boolean) or null value. The class contains the entire API for generating, parsing and enumerating JSON. The JsonToDataType and DataTypeToJson functions can be used to convert between JSON and DataFlex struct/array data.

Parse (ParseString)

Parses a string of JSON into a JSON object structure where the object instance on which it is called represents the outermost JSON object.

  • Params: String sData
  • Returns: Boolean

Use ReportParseError or the psParseError property to get parsing errors if False is returned.

ParseUtf8

Parses a UChar[] containing UTF-8 encoded JSON into a JSON object structure where the object instance on which it is called represents the outermost JSON object.

  • Params: UChar[] aData
  • Returns: Boolean

Use ReportParseError or the psParseError property to get parsing errors if False is returned.

psParseError

If Parse or ParseUtf8 failed this property contains the error description.

  • Returns: String

ReportParseError

If Parse or ParseUtf8 failed, calling this procedure will trigger an error with the proper description.

Stringify

Generates the JSON String for the object structure being represented by the JSON objects.

  • Returns: String

StringifyUtf8

Generates the JSON string for the object structure and returns the data in a UChar[] as a UTF-8 encoded string.

  • Returns: UChar[]

DataTypeToJson

Converts a native DataFlex variable (usually a struct or array) into a JSON object structure representing that data. Use in combination with Stringify to produce a JSON string.

  • Params: data (struct or array)

JsonToDataType

Converts a JSON object structure into a native DataFlex variable (usually a struct or array). Use after Parse to deserialize JSON into DataFlex types.

  • Returns: native DataFlex struct/array

Member

Gets a member by its name or array index.

  • Params: String sJsMember
  • Returns: Handle to the member object

JsonType

Returns the type of the object as an integer constant.

  • Returns: Integer

IsOfJsonType

Determines if the object is of a specific type and returns True if it is. Types are passed as constants and can be object, string, integer, number, boolean or null (null is also the initial state of the object).

  • Params: Integer eType
  • Returns: Boolean

JsonValue (Get_JsonValue)

Fetches the value. If the object is of type object or array it returns the JSON as a string.

  • Returns: String

Set JsonValue (Set_JsonValue)

Sets the value of the object. The object needs to be initialized as a simple JSON type (jsonTypeBoolean, jsonTypeInteger, jsonTypeString, etc.). To initialize a newly created JSON object use InitializeJsonType.

  • Params: String sValue

MemberValue

Returns the value of a member of the JSON object. The member name (or array index) is passed as the sMember parameter.

  • Params: String sMember
  • Returns: String

IsMemberOfJsonType

Checks the JSON type of a specific member (of an array or object) against the passed type. If the type passed matches the type of the member, True will be returned. Note that False will be returned if the member does not exist (no error).

  • Params:
  • String sMember
  • Integer eType
  • Returns: Boolean

MemberJsonType

Determines the JSON type of a specific member of an array or object.

  • Params: String sMember
  • Returns: Integer

InitializeJsonType

Initializes the JSON object as a specific type. This completely reinitializes the object allowing the object instance to be reused. If the previous value/object was a member of another JSON object it will continue to exist; otherwise it will be destroyed.

  • Params: Integer eType

HasMember

Checks if a member exists based on the passed member name (or array index). Note that a member of type null will return True.

  • Params: String sMember
  • Returns: Boolean

SetMember

Sets the member of an object or array to an initialized JSON object.

  • Params:
  • String sMember
  • Handle hoJsonObj

SetMemberValue

Sets the member of an object directly to a simple value. The JSON type needs to be passed explicitly.

  • Params:
  • String sMember
  • Integer eJsonType
  • String sValue

AddMember

Appends an initialized JSON object to an array.

  • Params: Handle hoJsonObj

AddMemberValue

Appends a simple value to an array.

  • Params:
  • Integer eJsonType
  • String sValue

RemoveMember

Removes a member from a JSON object or array. Removing from an array will shift all members after this member one place to the left.

  • Params: String sMember

MemberCount

Returns the number of members of an array or an object.

  • Returns: Integer (number of members)

MemberByIndex

Gets a member object by its index. Can be used for enumerating object properties and to access array items.

  • Params: Integer iIndex
  • Returns: JSON object Handle

MemberNameByIndex

Gets the name of an object member by its index. Used for enumerating members of a JSON object.

  • Params: Integer iIndex
  • Returns: String with the member name

peWhitespace

This property controls if/how whitespace is added to the generated JSON when calling the Stringify function. It should be set on the object where Stringify is called; setting it on parent objects does not affect the output.

  • Default: jpWhitespace_Plain

Options:

  • jpWhitespace_Plain — No whitespace is added. The most compact setting.
  • jpWhitespace_Spaced — Spaces are added between property names and their values.
  • jpWhitespace_Pretty — The most readable format with line feeds and indentation spaces.

pbUseVariantMetaData

This property determines how variants are serialized. Variants can contain several types of values and when serialized to JSON without adding metadata they cannot always be properly deserialized with the proper typed value. For example, when a variant containing a struct value is serialized with pbUseVariantMetaData set to False, it serializes the struct but when deserializing it doesn't know what struct type was used and so cannot reinitialize the proper struct.

Setting this property to True will serialize variants into JSON objects with a property vt indicating the variant type and, in the case of a struct, a property st with the name of the struct that was used. In the case of an array, a property at will be added indicating the array type.

When the JSON is used to serialize data from memory for storage, this property can be enabled to provide proper support for variants. It needs to be set on the object on which DataTypeToJson and JsonToDataType are being called.

pbRequireAllMembers

This property determines if JsonToDataType will raise errors if a struct member does not exist in the JSON structure. If set to False, a missing member in the JSON will not cause an error and leaves the struct member uninitialized.


DataFlex class definition (codesense / metadata)

The code below shows the DataFlex definition of the class as used by the metadata system to feed codesense:

Class cJsonObject is a cObject
{ MethodType=Property }
Function peWhitespace Returns Integer
End_Function

{ MethodType=Property }
{ EnumList="jpWhitespace_Plain,jpWhitespace_Spaced,jpWhitespace_Pretty" }
{ InitialValue=jpWhitespace_Plain }
Procedure Set peWhitespace Integer eValue
End_Procedure

{ MethodType=Property }
Function pbUseVariantMetaData Returns Boolean
End_Function

{ MethodType=Property }
{ InitialValue=False }
Procedure Set pbUseVariantMetaData Boolean bValue
End_Procedure

Function Parse String sData Returns Boolean
End_Function

Function ParseUtf8 UChar[] sData Returns Boolean
End_Function

Function Stringify Returns String
End_Function

Function StringifyUtf8 Returns UChar[]
End_Function

Procedure DataTypeToJson Struct data
End_Procedure

Function JsonToDataType Returns Struct
End_Function

Function Member String sMember Returns Handle
End_Function

Function JsonType Returns Integer
End_Function

Function IsOfJsonType Integer eType Returns Boolean
End_Function

{ MethodType=Property }
Function JsonValue Returns String
End_Function

{ MethodType=Property }
Procedure Set JsonValue String sValue
End_Procedure

Function MemberValue String sMember Returns String
End_Function

Function IsMemberOfJsonType String sMember Integer eType Returns Boolean
End_Function

Function MemberJsonType String sMember Returns Integer
End_Function

Procedure InitializeJsonType Integer eType
End_Procedure

Procedure SetMember String sMember Handle hoJsonObj
End_Procedure

Procedure SetMemberValue String sMember Integer eJsonType String sValue
End_Procedure

Procedure AddMember Handle hoJsonObj
End_Procedure

Procedure AddMemberValue Integer eJsonType String sValue
End_Procedure

Function MemberCount Returns Integer
End_Function

Function MemberByIndex Integer iIndex Returns Handle
End_Function

Function MemberNameByIndex Integer iIndex Returns String
End_Function

End_Class