Skip to content

JSON Parsing and Transfer

The JSON parser is represented by a single cJSONObject class that contains the entire API for parsing, generating, analyzing, and manipulating JSON. The API is 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 and then using the API to analyze/enumerate the object structure.

A special feature of the DataFlex 2017/19.0 JSON Parser is the ability to generate the JSON Object structure based on struct data in memory. Calling DataTypeToJson will generate cJSONObject objects that match the structure of the struct, and calling Stringify will fully serialize this data into a string. The reverse is done by using JsonToDataType to convert the JSON Object structure into struct data. This feature requires the JSON to match the format of the struct data, or errors will be thrown.

Code Examples

Note: The WebOrder sample has a view that shows how to use the new JSON capabilities. This is found under “Views / Comment.” This demonstrates how to use a “REST-ful” JSON service. It uses the cJsonObject class and the cJsonHttpTransfer class to interact with a remote JSON service. The DataFlex code that runs all of this can be found in GetComments.wo and DisplayUser.wo.

While this was created to demonstrate JSON usage, it also provides a good example of how a client-side REST/JSON service like this can be visually integrated into a Web Application. It would have been equally simple to have built the same sample into a Windows Application.

Example: Parsing JSON String

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

The input would be:

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

Output should be:

Value 1: Test data!
Value 2: 381
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 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: Simplified JSON Parsing

Of course, the DataFlex syntax of working with dynamic objects is a bit clumsy, so we might simplify the sample above by providing functions like IsMemberOfJsonType and MemberValue to provide direct access to simple members. This example shows how to use these to implement the same sample as shown above.

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

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

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

        Get IsMemberOfJsonType of hoObj "Value2" jsonTypeInteger to bIsJsonType
        If (bIsJsonType) Begin
            Get MemberValue of hoObj "Value2" to iVal
            Showln ("Value 2: " + iVal)
        End
    End
    Send Destroy of hoObj
End_Procedure

Example: Generating JSON

This example shows how to create a JSON object structure to generate JSON into a string. It does reuse 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: Optimized JSON Generation

The optimized version of generating the same object could be:

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: Generating JSON Array

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: Optimized JSON Array Generation

This could be optimized with an AppendMemberValue 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: Traversing JSON Objects

The more extensive example below shows how one would traverse a tree of JSON objects. It knows the difference between arrays, objects, and the other simple types. It outputs the structure onto 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: Deserializing JSON Data

The following example shows how to deserialize JSON data into a specific struct type.

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

    Get Create (RefClass(cJSONObject)) to hoObj
    Get ParseString of hoObj sData to bSuccess

    If (bSuccess) Begin
        Get JsonToDataType of hoObj to tResult
    End
    Send Destroy of hoObj

    Function_Return tResult
End_Function

Example: Serializing Struct into JSON

The following example 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: Altering JSON Object

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

cJsonObject

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 parse a JSON document into struct and/or arrays.

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 is dependent on the cJsonObject class. It is expected that this class always uses cJsonObject objects 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 common HTTP verbs: HttpDeleteJson, HttpGetJson, HttpPatchJson, HttpPostJson, and HttpPutJson. An additional method HttpVerbJson can be used to pass any HTTP 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 what would be expected 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. Also 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.