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
- Parse known JSON (extensive)
- Parse known JSON (optimized)
- Generate JSON (long)
- Generate JSON (optimized)
- Generate JSON Array (long)
- Generate JSON Array (short)
- Parse unknown JSON
- Deserialize JSON into struct
- Serialize struct into JSON
- Manipulate JSON Object
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
cJsonObjectclass and the cJsonHttpTransfer class to interact with a remote JSON service. The DataFlex code that runs all of this can be found inGetComments.woandDisplayUser.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.