You will encounter more applications in the future that rely on the compact JSON format as the format for exporting or importing data. The data is serialised as JSON text.
For processing data in JSON format, the JSON.Decode(…) method implements both a validator and a parser for a JSON text. Data in JSON format becomes data of the Gambas type Variant and can be of the type Collection or JSONCollection.
For example, the following JSON text with weather data is very compact:
{"coord":{"lon":11.77,"lat":52.78},"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon": "02d"}], "base":"cmc stations","main":{"temp":12.48,"pressure":1017.3,"humidity":85,"temp_min":12.48, "temp_max":12.48, "sea_level":1021.8,"grnd_level":1017.3},"wind":{"speed":6.67,"deg":242.504},"clouds":{"all":24},"dt":1459947946, "sys":{"message":0.0075,"country":"DE","sunrise":1459917197,"sunset": 1459965476},"id":2856639,"name":"Osterburg","cod":200}
The same text is much easier to read if it is output formatted. Since the formatted JSON text is well structured, it is also easy to read:
{ coord: { lon: 11,77, lat: 52,78 }, weather: [ { id: 801, main: "Clouds", description: "few clouds", icon: "02d" } ], base: "cmc stations", main: { temp: 12,48, pressure: 1017,3, humidity: 85, temp_min: 12,48, temp_max: 12,48, sea_level: 1021,8, grnd_level: 1017,3 }, wind: { speed: 6,67, deg: 242,504 }, clouds: { all: 24 }, dt: 1459947946, sys: { message: 0,0075, country: "DE", sunrise: 1459917197, sunset: 1459965476 }, id: 2856639, name: "Osterburg", cod: 200 }
There is no method in Gambas for the formatted display of data in JSON format. In this project, you will therefore be presented with two variants of how to output a JSON text in a formatted way.
The RFC text says that a JSON value is constructed like this:
value = object | array | number | string | 'true' | 'false' | 'null'
An array, for example, is described according to the grammar like this:
array = "[" [value *("," value)] "]"
This description, along with the others, can be carried over into the following Gambas procedures:
Private Sub PutValue() txaOutput.Insert(Space$(iIndent) & IIf(sKey, "\"" & sKey & "\"" & ": ", "")) Select Case TypeOf(vValue) Case gb.Object If vValue Is JSONCollection Then PutObject() Else If vValue Is Variant[] Then PutArray() Endif Case gb.Integer, gb.Long, gb.Float PutNumber() Case gb.String PutString() Case gb.Boolean PutBoolean() Case gb.Null PutNull() End Select End ' PutValue() Private Sub PutObject() Dim vElement As Variant, iElement As Integer, jCol As JSONCollection = vValue txaOutput.Insert("{" & gb.NewLine) iIndent += 2 iElement = 1 For Each vElement In jCol sKey = jCol.Key vValue = vElement PutValue() txaOutput.Insert(IIf(iElement < jCol.Count, ",", "") & gb.NewLine) Inc iElement Next iIndent -= 2 txaOutput.Insert(Space$(iIndent) & "}") End ' PutObject() Private Sub PutArray() Dim iIndex As Integer, aArray As Variant[] = vValue txaOutput.Insert("[" & gb.NewLine) iIndent += 2 For iIndex = 0 To aArray.Max sKey = "" vValue = aArray[iIndex] PutValue() txaOutput.Insert(IIf(iIndex < aArray.Max, ",", "") & gb.NewLine) Next iIndent -= 2 txaOutput.Insert(Space$(iIndent) & "]") End ' PutArray() Private Sub PutBoolean() txaOutput.Insert(IIf(vValue, "true", "false")) End ' PutBoolean() Private Sub PutNumber() txaOutput.Insert(Str$(vValue)) End ' PutNumber() Private Sub PutString() txaOutput.Insert(Subst$("\"&1\"", vValue)) End ' PutString() Private Sub PutNull() txaOutput.Insert("null") End ' PutNull()
Note that the project source code maps the JSON grammar 1:1. A non-terminal symbol in the grammar - for example, string - always corresponds to a function that recognises and outputs the non-terminal symbol, with the functions calling each other.
The procedure EditData(…) is passed the JSON text as a parameter, which is decoded and then available in the global variable vValue of the procedure PutValue():
Private Sub EditData(Data As String) Dim jCollection As JSONCollection Try jCollection = JSON.Decode(Data, True) ' VALIDATOR If Error Then Message.Error("<b>Error decoding!</b><br>" & Error.Text & " Error-Code = " & Error.Code) Return Endif sKey = Null vValue = jCollection iIndent = 0 PutValue() End ' EditData(Data As String) <code> Ausgelöst wird die strukturierte Anzeige des JSON-Textes durch die folgende Prozedur: <code gambas> Public Sub btnFormattingJSON_Click() txaOutput.Clear() EditData(txaInput.Text) End ' btnFormattingJSON_Click()
Figure 24.6.9.1.1: Formatted Display - Variant 1
The formatted JSON text is displayed if the JSON text is syntactically OK.
Try jCollection = JSON.Decode(Data, True) ' VALIDATOR
If an error occurs when decoding the JSON text, this is displayed - the project thus also contains a validator for JSON text:
Figure 24.6.9.1.2: Error message (validator)
Project example 3 shows the JSON text for the data of a TreeView. In the → Chapter 17.9 TreeView project, the data is exported and imported in JSON format. This makes it possible to access the exported elements of the TreeView at any time during the testing of the project and to generate and display the elements of the TreeView from the imported data:
Figure 24.6.9.1.3: GUI for testing properties and methods of a TreeView.
You can also achieve the formatted output of JSON text - sorted by the individual key values - like this:
Public Sub btnFormattingJSONPython_Click() txaOutput.Clear() Try Shell "echo '" & txaInput.Text & "' | python -m json.tool" To txaOutput.Text txaOutput.Pos = 0 End ' btnFormattingJSONPython_Click()
An extension was included in the project to help you determine and display the mapping between the key and the value type of the decoded JSON text (Collection or JSONCollection type). You will only realise the advantage of using the procedure GetStructure(…) and the function GetType(…) when you try out the project in → Chapter 24.6.9.2:
Private Sub GetStructure(Data As String) Dim i As Integer = 1, vVariant As Variant Dim jCollection As JSONCollection Try jCollection = JSON.Decode(Data, True) If Error Then Message.Error("<b>Error decoding!</b><br>" & Error.Text & " Error-Code = " & Error.Code) Return Endif txaOutput.Insert(gb.NewLine) txaOutput.Insert("Anzahl der Elemente in der JSON-Collection: " & jCollection.Count & gb.NewLine) txaOutput.Insert(gb.NewLine) txaOutput.Insert(String$(100, "-") & gb.NewLine & gb.NewLine) For Each vVariant In jCollection txaOutput.Insert(("Key ") & Str(i) & " : \"" & jCollection.Key & "\"" & (" ---> Value-Type: ") & / GetType(vVariant) & gb.NewLine) Inc i Next End ' GetStructure(...) Private Function GetType(Value As Variant) As String Select Case TypeOf(Value) Case gb.Boolean ' 1 Return "Boolean" Case gb.Integer ' 4 Return "Integer" Case gb.Long ' 5 Return "Long-Integer" Case gb.Float ' 7 Return "Float" Case gb.String '9 Return "String" Case gb.Null ' 15 Return "NULL OR JSON-Null" Case gb.Object ' 16 If Value Is JSONCollection Then Return "JSONCollection" If Value Is Collection Then Return "Collection" If Value Is Variant[] Then Return "Variant[]" End Select Return "?" End ' GetType(...)