18.4.0 released Sep 25, 2023
Read json

Purpose: Parse JSON text and get values.

// Random access to JSON data:

read-json <json> \
    ( key <key> ) | ( key-list <key fragment> [ , <key fragment> ] ) \
    value [ define ] <value> \
    [ status [ define ] <status> ] \
    [ type [ define ] <type> ]

// Sequential access to JSON data:

read-json <json> traverse begin

read-json <json> traverse \
    key [ define ] <key> \
    value [ define ] <value> \
    [ status [ define ] <status> ] \
    [ type [ define ] <type> ]

read-json will obtain the values from <json> variable that was produced by parsing JSON text with new-json.
Without "traverse" clause
key/value pairs are accessed with "key" clause specifying a quoted normalized key name, which is the value's name preceded with the names of all objects and array members leading up to it, separated by a dot ("."), with each name quoted. The actual <value> is obtained with "value" clause, and the <type> of value can be obtained with optional "type" clause.

Instead of "key" clause, you can also use "key-list" clause which specifies a list of string <key fragment>s. Those fragments, when concatenated, should form the same value as <key> if "key" clause were used. If any of <key fragment>s is a NULL value, then the key ends with it. Note that a key with NULL as the very first key fragment is an invalid key.

Note that node names in JSON are always quoted, so their quotes must be escaped within a key (or a key fragment), which itself is a string between quotes. For instance, JSON key "some key" must be specified as "\"some key\"" to be a proper string. An empty key would be "\"\"".

The <status> variable in "status" clause is either VV_OKAY on success (meaning value is obtained) or VV_ERR_EXIST if not found. The value is stored in string variable <value> specified in "value" clause.  

<value>, <type> and <status> can be created with optional "define".

Optional number <type> in "type" clause will contain the type of data, which can be VV_JSON_TYPE_STRING, VV_JSON_TYPE_NUMBER, VV_JSON_TYPE_REAL, VV_JSON_TYPE_BOOL or VV_JSON_TYPE_NULL  for a string, integer, decimal, boolean and null value respectively.
With "traverse" clause
This allows sequential access to JSON values. No key is specified, rather key/value pairs are obtained in sequence from beginning to the end. <key> can be created with optional "define". The other clauses used are the same as without "traverse".
Random access via built-in hash
You can access randomly any number, string, boolean or null value within a JSON document without having to create a matching structure in your program. Internally, Vely creates a hash table for fast and direct access to any key/value pair (see new-json).
Keys (normalized names)
<key> in "key" clause is a normalized name of any given leaf node in JSON text. This means every non-leaf node is included (such as arrays and objects), separated by a dot ("."), and arrays are indexed with "[]". An example would be:
"menu"."popup"."menuitem"[1]."onclick"

For instance, the above is a normalized name in this JSON text with the value of "OpenDoc()":
{"menu": {
  "id": "file",
  "value": "File",
  "popup": {
    "menuitem": [
      {"value": "New", "onclick": "CreateNewDoc()"},
      {"value": "Open", "onclick": "OpenDoc()"},
      {"value": "Close", "onclick": "CloseDoc()"}
    ]
  }
}

the normalized names for all leaf nodes are:
"menu"."id"
"menu"."value"
"menu"."popup"."menuitem"[0]."value"
"menu"."popup"."menuitem"[0]."onclick"
"menu"."popup"."menuitem"[1]."value"
"menu"."popup"."menuitem"[1]."onclick"
"menu"."popup"."menuitem"[2]."value"
"menu"."popup"."menuitem"[2]."onclick"

The code to parse this JSON text might then look like:
// Json text
char json_text[] =\
"{\"menu\":\
{\"id\": \"file\",\
\"value\": \"File\",\
\"popup\":\
    {\"menuitem\":\
        [{\"value\": \"New\", \"onclick\": \"CreateNewDoc()\"},\
        {\"value\": \"Open\", \"onclick\": \"OpenDoc()\"},\
        {\"value\": \"Close\", \"onclick\": \"CloseDoc()\"}\
        ]\
    }\
}\
}\n";

// Parse json text
new-json define json_var from json_text status define st error-text define errt error-position define errp
if (st != VV_OKAY) {
    @Could not parse json, error <<p-out errt>> at position <<p-num errp>>
} else {
    @Json parsed okay.
}

// Get value based on key
read-json json_var key "\"menu\".\"popup\".\"menuitem\"[1].\"onclick\"" value define val status st
if (st != VV_OKAY) {
    @Could not find json key
} else {
    @Key value is <<p-out val>>
}

The result is:
Json parsed okay.
Key value is OpenDoc()

Values
All values are always returned as strings. This is because in many cases that's how they are used in an application and converting them to other types (like numbers) and back again would affect performance.

Vely checks type validity - for example an invalid integer or decimal number will produce an error. If you need to convert a value to a number you can use C's library functions like atoll() or atof().

String values are returned encoded as UTF8, and any escaped characters (like \n or \t) are converted to their binary equivalent (use "noencode" in new-json to skip this). Such encoded strings can be output anywhere, from a terminal to a client (like browser).
Walk through JSON document
Use "traverse" clause to access JSON nodes sequentially, one by one, from the beginning to the end. Use "traverse begin" to rewind to the beginning, and then read data using "traverse" with "key" clause.

The following code loops through all the leaf nodes of a JSON document - you can also use it to examine a JSON document with unknown structure:
// Parse JSON
new-json define jv from json_text

// Position at the beginning of the JSON document
read-json jv traverse begin

// Loop through all JSON leaf nodes, obtain normalized name, type and value
while (1)
{
    // Get nodes' key, value and type. Check status for end of loop detection.
    read-json jv traverse key define k value define v type define t status define s

    if (s != VV_OKAY) break; // end of loop

    // Output name, value and type
    pf-out "Name [%s], value [%s], type [%lld]\n", k, v, t
}

Examples
Obtaining a value with "key" clause:
read-json jv key "\"glossary\".\"title\"" value d

The same with "key-list" - the key can be split in any way that works for your application:
read-json jv key-list "\"glossary\"" , ".\"title\"" value d

See also
JSON
delete-json  
new-json  
read-json    
See all
documentation


You are free to copy, redistribute and adapt this web page (even commercially), as long as you give credit and provide a link back to this page (dofollow) - see full license at CC-BY-4.0. Copyright (c) 2019-2023 Dasoftver LLC. Vely and elephant logo are trademarks of Dasoftver LLC. The software and information on this web site are provided "AS IS" and without any warranties or guarantees of any kind.