Json API

The AngelScript JSON API provides robust parsing and encoding of JSON directly into native AngelScript structures. JSON objects map to dictionary objects, using standard AngelScript types.

This API mirrors the Lua JSON API and is designed to behave identically where possible.


Overview

Available functions:

Function
Description

json_parse(text, result, error)

Parse JSON text into a dictionary

json_decode(text, result, error)

Alias of json_parse

json_stringify(value, json, error)

Convert a dictionary into a JSON string

json_encode(value, json, error)

Alias of json_stringify

All functions return true on success and false on failure, with detailed error messages.


JSON → Dictionary

bool json_parse(const string &in text, dictionary &out result, string &out error)

bool json_decode(const string &in text, dictionary &out result, string &out error)

Parses a JSON string and converts it into a nested AngelScript dictionary.

Parameters

Name
Type
Description

text

string

JSON text to parse

result

dictionary

Output dictionary filled with parsed values

error

string

Error message if the function fails

JSON → AngelScript Type Mapping

JSON
AngelScript

Object {}

dictionary (recursively nested)

String "text"

string

Number 123, 4.5

double

Boolean true/false

double (1.0/0.0)

null

field omitted

Array [...]

(currently skipped)

Example

dictionary d;
string err;

bool ok = json_parse("{\"a\":1, \"b\":\"hello\"}", d, err);
if (!ok)
{
    log_error("JSON parse failed: " + err);
    return;
}

double a;
string b;

d.get("a", a);    // 1.0
d.get("b", b);    // "hello"

Dictionary → JSON

bool json_stringify(const dictionary &in value, string &out json, string &out error)

bool json_encode(const dictionary &in value, string &out json, string &out error)

Converts a dictionary (and any nested dictionaries) into valid JSON text.

Parameters

Name
Type
Description

value

dictionary

The object to convert into JSON

json

string

Output JSON text

error

string

Error message if the function fails

AngelScript → JSON Type Mapping

AngelScript
JSON

double

number

string

string

bool

boolean

dictionary@

JSON object

null dictionary@

null

(any other type)

error

Example

dictionary d;
d.set("name", "Player1");
d.set("score", 120.0);
d.set("alive", true);

string json;
string err;

if (!json_stringify(d, json, err))
{
    log_error("Stringify failed: " + err);
}
else
{
    log("JSON: " + json);
}

Output:

{"name":"Player1","score":120,"alive":true}

Nesting Example

JSON objects automatically map to nested dictionaries:

string text = """
{
    "player": {
        "name": "Alice",
        "pos": { "x": 10, "y": 20 }
    }
}
""";

dictionary root;
string err;

json_parse(text, root, err);

dictionary@ player;
root.get("player", @player);

string name;
dictionary@ pos;
double px, py;

player.get("name", name);
player.get("pos", @pos);
pos.get("x", px);
pos.get("y", py);

Null Behavior

JSON null becomes nonexistent key:

{"a":1,"b":null,"c":2}

Becomes:

d.exists("a") == true
d.exists("b") == false
d.exists("c") == true

Unsupported Types

The AngelScript dictionary can technically store any type, but the JSON stringifier only supports:

  • double

  • string

  • bool

  • dictionary@

Anything else causes:

json_stringify: unsupported value type in dictionary

Arrays

JSON arrays ([ ... ]) are currently skipped. If you need them, support can be added using array<T> or CScriptArray.


Full Roundtrip Example

dictionary d;
d.set("value", 123.0);
d.set("name", "Test");
d.set("ok", true);

dictionary inner;
inner.set("x", 10.0);
inner.set("y", 20.0);
d.set("pos", @inner);

string json, err;
json_stringify(d, json, err);

// parse it back
dictionary back;
json_parse(json, back, err);

Full API Test

void DumpDict(const string &in label, dictionary &in d)
{
    log(label);
    
    array<string>@ keys = d.getKeys();
    for (uint i = 0; i < keys.length(); i++)
    {
        string k = keys[i];
        // Try double first
        double dv;
        string sv;
        bool gotDouble = d.get(k, dv);
        bool gotString = d.get(k, sv);
        
        string vStr;
        if (gotDouble)
        vStr = "double " + k + " = " + dv;
        else if (gotString)
        vStr = "string " + k + " = \"" + sv + "\"";
        else
        vStr = "key " + k + " (unsupported type in dump)";
        
        log("  " + vStr);
    }
}

void TestBasicParse()
{
    log("[1] Basic json_parse / json_decode");
    
    const string jsonText = """
    {
        "number": 123,
        "float":  3.14,
        "bool_true": true,
        "bool_false": false,
        "text": "hello",
        "object": { "x": 10, "y": 20 }
    }
    """;
    
    dictionary d;
    string err;
    
    if (!json_parse(jsonText, d, err))
    {
        log_error("  [FAIL] json_parse failed: " + err);
        return;
    }
    log("  [ OK ] json_parse succeeded");
    DumpDict("  Dump of parsed dictionary:", d);
    
    // Basic checks
    double num = 0;
    double flt = 0;
    string txt;
    
    if (d.get("number", num) && num == 123)
    log("  [ OK ] number == 123");
    else
    log_error("  [FAIL] number != 123");
    
    if (d.get("float", flt) && flt > 3.13 && flt < 3.15)
    log("  [ OK ] float ~= 3.14");
    else
    log_error("  [FAIL] float ~= 3.14");
    
    if (d.get("text", txt) && txt == "hello")
    log("  [ OK ] text == \"hello\"");
    else
    log_error("  [FAIL] text != \"hello\"");
    
    // bools are stored as doubles (1.0 / 0.0)
    double bt = 0, bf = 1;
    d.get("bool_true", bt);
    d.get("bool_false", bf);
    if (bt == 1.0 && bf == 0.0)
    log("  [ OK ] bool_true/false mapped to 1.0/0.0");
    else
    log_error("  [FAIL] bool mapping incorrect");
    
    // Test alias json_decode
    dictionary d2;
    string err2;
    if (json_decode("{\"a\":1,\"b\":2}", d2, err2))
    log("  [ OK ] json_decode succeeded");
    else
    log_error("  [FAIL] json_decode failed: " + err2);
}

void TestInvalidParse()
{
    log("\n[2] Invalid JSON parse test");
    
    dictionary d;
    string err;
    
    const string badJson = "{ invalid json !!! }";
    bool ok = json_parse(badJson, d, err);
    if (!ok)
    {
        log("  [ OK ] json_parse returned false for invalid JSON");
        log("        error: " + err);
    }
    else
    {
        log_error("  [FAIL] json_parse unexpectedly succeeded on invalid JSON");
    }
}

void TestStringifyRoundtrip()
{
    log("\n[3] json_stringify / json_encode roundtrip");
    
    dictionary d;
    d.set("hello", "world");
    d.set("num", 42.0);          // numbers are stored as double
    d.set("float", 1.5);
    d.set("flag_bool", true);    // bool (script side)
    d.set("flag_num", 1.0);      // numeric boolean
    
    // nested object
    dictionary nested;
    nested.set("x", 10.0);
    nested.set("y", 20.0);
    d.set("nested", @nested);
    
    DumpDict("  Original dictionary:", d);
    
    string json;
    string err;
    
    if (!json_stringify(d, json, err))
    {
        log_error("  [FAIL] json_stringify failed: " + err);
        return;
    }
    
    log("  [ OK ] json_stringify succeeded");
    log("  JSON: " + json);
    
    // Roundtrip: parse back
    dictionary back;
    string err2;
    if (!json_parse(json, back, err2))
    {
        log_error("  [FAIL] parse(stringify(...)) failed: " + err2);
        return;
    }
    
    log("  [ OK ] parse(stringify(...)) succeeded");
    DumpDict("  Decoded dictionary:", back);
    
    // Test alias json_encode
    string json2, err3;
    if (json_encode(d, json2, err3))
    log("  [ OK ] json_encode succeeded: " + json2);
    else
    log_error("  [FAIL] json_encode failed: " + err3);
}

void TestNullAndArrays()
{
    log("\n[4] Null and array handling note");
    
    const string txt = """
    {
        "a": 1,
        "b": null,
        "c": [1,2,3]
    }
    """;
    
    dictionary d;
    string err;
    
    if (!json_parse(txt, d, err))
    {
        log_error("  [FAIL] parse failed: " + err);
        return;
    }
    
    DumpDict("  Parsed dictionary:", d);
    
    // "b" is null -> omitted
    double a = 0;
    bool hasB = d.exists("b");
    d.get("a", a);
    
    if (a == 1.0 && !hasB)
    log("  [ OK ] null field skipped, 'a' present");
    else
    log_error("  [FAIL] null or 'a' handling incorrect");
    
    // "c" is an array -> currently skipped
    if (!d.exists("c"))
    log("  [ OK ] array key 'c' skipped as expected");
    else
    log_error("  [FAIL] array key 'c' unexpectedly present");
}

int main()
{
    log("=== JSON API Test Begin (AngelScript) ===");
    
    TestBasicParse();
    TestInvalidParse();
    TestStringifyRoundtrip();
    TestNullAndArrays();
    
    log("=== JSON API Test End ===");
    return 0;
}

Summary of Functions

Function
Description

json_parse(text, result, error)

Parse JSON into dictionary

json_decode(text, result, error)

Alias of parse

json_stringify(value, json, error)

Convert dictionary to JSON

json_encode(value, json, error)

Alias of stringify

Last updated