Skip to content

NimaBastani/pawn-json-parser

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 

Repository files navigation

JSON Parser for SA-MP/open.mp (Pawn)

A comprehensive, pure-Pawn JSON parser, builder, serializer, and query engine for SA-MP/open.mp servers. No plugins required.

About This Project

This library has been a quiet side project of mine for roughly 2 years. I started it because I needed a reliable JSON solution for my SA-MP server and nothing out there felt right - either too limited, or required external plugins I didn't want to depend on.

So I built my own, piece by piece, version by version. It was never meant to be public. Just a personal tool I kept improving whenever I hit a new edge case or needed a feature that wasn't there yet (or if I had an exam the day after).

But at some point I looked at it and thought... why not share it? Maybe someone else is in the same spot I was. So here it is, nothing fancy, just a solid pure-Pawn JSON parser that does what it's supposed to do.


Table of Contents


Installation

  1. Copy json_parser.inc into your pawno/include/ directory.
  2. Include it in your script:
#include <json_parser>

No plugins, no external dependencies. Pure Pawn.


Configuration

All limits can be overridden before including the file using #define:

Define Default Description
JSON_MAX_NODES 2048 Maximum number of JSON nodes across all documents
JSON_MAX_KEY_LEN 64 Maximum length of a key string
JSON_MAX_VALUE_LEN 256 Maximum length of a value string
JSON_MAX_DEPTH 32 Maximum nesting depth during parsing
JSON_MAX_DOCUMENTS 16 Maximum number of simultaneous documents
JSON_MAX_STRING_LEN 4096 Maximum input JSON string length for parsing
JSON_SERIALIZE_BUFFER 4096 Internal buffer size for serialization output

Example override:

#define JSON_MAX_NODES 4096
#define JSON_MAX_STRING_LEN 8192
#include <json_parser>

Quick Start

Parsing JSON

new docId;
new jsonStr[] = "{\"name\":\"John\",\"age\":25,\"vip\":true}";

if (JSON_Parse(jsonStr, docId))
{
    new root = JSON_GetRootNode(docId);

    new name[32];
    JSON_GetStringByPath(root, "name", name);
    // name = "John"

    new age;
    JSON_GetIntByPath(root, "age", age);
    // age = 25

    new bool:vip;
    JSON_GetBoolByPath(root, "vip", vip);
    // vip = true

    JSON_DestroyDocument(docId);
}

Building JSON

new docId, root;
root = JSON_BuildObject(docId);

JSON_ObjectSetString(root, "server", "My SA-MP Server");
JSON_ObjectSetInt(root, "maxPlayers", 100);
JSON_ObjectSetBool(root, "active", true);

new output[512];
JSON_Prettify(root, output);
print(output);

JSON_DestroyDocument(docId);

Output:

{
  "server": "My SA-MP Server",
  "maxPlayers": 100,
  "active": true
}

Function Quick Reference

Function Returns Description
JSON_Init() 1 Initialize parser state
JSON_Cleanup() 1 Free all documents and reset state
JSON_CreateDocument() docId Create empty document
JSON_DestroyDocument(docId) 0/1 Destroy document and free nodes
JSON_GetRootNode(docId) nodeId Get root node of document
JSON_Parse(str, &docId) 0/1 Parse JSON string
JSON_ParseFile(file, &docId) 0/1 Parse JSON file
JSON_Validate(str) bool Check if string is valid JSON
JSON_GetErrorMessage(dest) - Get last parse error
JSON_GetNodeType(nodeId) type Get node type constant
JSON_GetNodeTypeName(nodeId, dest) 0/1 Get type as string
JSON_GetNodeKey(nodeId, dest) 0/1 Get node key
JSON_GetChildCount(nodeId) count Count direct children
JSON_IsNull(nodeId) bool Check if null
JSON_IsString(nodeId) bool Check if string
JSON_IsInt(nodeId) bool Check if integer
JSON_IsFloat(nodeId) bool Check if float
JSON_IsBool(nodeId) bool Check if boolean
JSON_IsObject(nodeId) bool Check if object
JSON_IsArray(nodeId) bool Check if array
JSON_IsNumber(nodeId) bool Check if int or float
JSON_GetString(nodeId, dest) 0/1 Get string value
JSON_GetInt(nodeId, &val) 0/1 Get integer value
JSON_GetFloat(nodeId, &val) 0/1 Get float value
JSON_GetBool(nodeId, &val) 0/1 Get boolean value
JSON_GetParent(nodeId) nodeId Get parent node
JSON_GetFirstChild(nodeId) nodeId Get first child
JSON_GetLastChild(nodeId) nodeId Get last child
JSON_GetNextSibling(nodeId) nodeId Get next sibling
JSON_GetPrevSibling(nodeId) nodeId Get previous sibling
JSON_GetObjectChild(nodeId, key) nodeId Find child by key
JSON_ObjectHasKey(nodeId, key) bool Check if key exists
JSON_GetArrayElement(nodeId, idx) nodeId Get array element by index
JSON_GetArraySize(nodeId) count Get array length
JSON_GetByPath(nodeId, path) nodeId Navigate by dot path
JSON_GetStringByPath(nodeId, path, dest) 0/1 Get string by path
JSON_GetIntByPath(nodeId, path, &val) 0/1 Get int by path
JSON_GetFloatByPath(nodeId, path, &val) 0/1 Get float by path
JSON_GetBoolByPath(nodeId, path, &val) 0/1 Get bool by path
JSON_SetByPath(docId, nodeId, path, val) 0/1 Set node by path
JSON_SetStringByPath(docId, nodeId, path, val) 0/1 Set string by path
JSON_SetIntByPath(docId, nodeId, path, val) 0/1 Set int by path
JSON_SetFloatByPath(docId, nodeId, path, val) 0/1 Set float by path
JSON_SetBoolByPath(docId, nodeId, path, val) 0/1 Set bool by path
JSON_RemoveByPath(nodeId, path) 0/1 Remove node by path
JSON_EnsurePath(docId, nodeId, path) nodeId Create intermediate path nodes
JSON_CreateObject(docId) nodeId Create empty object node
JSON_CreateArray(docId) nodeId Create empty array node
JSON_CreateString(docId, val) nodeId Create string node
JSON_CreateInt(docId, val) nodeId Create integer node
JSON_CreateFloat(docId, val) nodeId Create float node
JSON_CreateBool(docId, val) nodeId Create boolean node
JSON_CreateNull(docId) nodeId Create null node
JSON_ObjectSetValue(nodeId, key, valNode) 0/1 Set key-value on object
JSON_ObjectSetString(nodeId, key, val) 0/1 Set string on object
JSON_ObjectSetInt(nodeId, key, val) 0/1 Set int on object
JSON_ObjectSetFloat(nodeId, key, val) 0/1 Set float on object
JSON_ObjectSetBool(nodeId, key, val) 0/1 Set bool on object
JSON_ObjectSetNull(nodeId, key) 0/1 Set null on object
JSON_ObjectRemoveKey(nodeId, key) 0/1 Remove key from object
JSON_ArrayAppend(nodeId, valNode) 0/1 Append to array
JSON_ArrayAppendString(nodeId, docId, val) 0/1 Append string to array
JSON_ArrayAppendInt(nodeId, docId, val) 0/1 Append int to array
JSON_ArrayAppendFloat(nodeId, docId, val) 0/1 Append float to array
JSON_ArrayAppendBool(nodeId, docId, val) 0/1 Append bool to array
JSON_ArrayInsert(nodeId, idx, valNode) 0/1 Insert into array
JSON_ArrayRemove(nodeId, idx) 0/1 Remove from array
JSON_BuildObject(&docId) nodeId Create doc with root object
JSON_BuildArray(&docId) nodeId Create doc with root array
JSON_Serialize(nodeId, dest, maxlen, pretty) 0/1 Serialize to string
JSON_Minify(nodeId, dest) 0/1 Compact serialization
JSON_Prettify(nodeId, dest) 0/1 Pretty serialization
JSON_SerializeToFile(nodeId, file, pretty) 0/1 Serialize to file
JSON_SerializeDoc(docId, dest, maxlen, pretty) 0/1 Serialize document
JSON_SaveDocument(docId, file, pretty) 0/1 Save document to file
JSON_CloneNode(docId, nodeId) nodeId Deep copy a node
JSON_MergeObjects(docId, target, source, overwrite) 0/1 Merge two objects
JSON_Equal(nodeA, nodeB) bool Deep equality check
JSON_FindByKey(nodeId, key) nodeId Search by key recursively
JSON_FindByStringValue(nodeId, val) nodeId Search by string value
JSON_FindByIntValue(nodeId, val) nodeId Search by int value
JSON_IterateObject(nodeId, prev) nodeId Manual object iteration
JSON_IterateArray(nodeId, prev) nodeId Manual array iteration
JSON_GetObjectKeys(nodeId, keys, max, maxlen) count Extract all keys
JSON_NodeToString(nodeId, dest) 0/1 Convert any value to string
JSON_NodeToInt(nodeId, &val) 0/1 Convert any value to int
JSON_NodeToFloat(nodeId, &val) 0/1 Convert any value to float
JSON_CountNodes(nodeId) count Count all nodes in subtree
JSON_GetMaxDepth(nodeId) depth Get max nesting depth
JSON_GetFreeNodeCount() count Get available node slots
JSON_DebugPrintNode(nodeId, depth) - Print debug tree to console

Core Concepts

Documents

A document is a container that owns a set of nodes. Every parsing or building operation starts with a document. Documents are identified by an integer ID.

  • Maximum simultaneous documents: JSON_MAX_DOCUMENTS (default 16)
  • Always destroy documents when done to free nodes

Nodes

Every JSON value (string, number, object, array, bool, null) is stored as a node. Nodes form a tree structure with parent-child and sibling relationships.

Each node has:

  • A type (object, array, string, int, float, bool, null)
  • A key (if it's a child of an object)
  • A value (for primitive types)
  • Links to parent, first child, last child, previous sibling, next sibling

Data Types

Constant Value Description
JSON_TYPE_NULL 0 JSON null
JSON_TYPE_STRING 1 String value
JSON_TYPE_INT 2 Integer value
JSON_TYPE_FLOAT 3 Float value
JSON_TYPE_BOOL 4 Boolean (true/false)
JSON_TYPE_OBJECT 5 JSON object {}
JSON_TYPE_ARRAY 6 JSON array []

API Reference

Initialization

JSON_Init()

Initializes the parser state. Called automatically by most functions. Safe to call multiple times.

JSON_Init();

Returns: 1 always.


Document Management

JSON_CreateDocument()

Creates a new empty document.

new docId = JSON_CreateDocument();
if (docId == JSON_INVALID_DOCUMENT)
{
    print("ERROR: No free document slots!");
}

Returns: Document ID on success, JSON_INVALID_DOCUMENT (-1) on failure.


JSON_DestroyDocument(docId)

Destroys a document and frees all its nodes.

JSON_DestroyDocument(docId);

Returns: 1 on success, 0 on failure.

Always call this when you're done with a document to prevent node leaks.


JSON_GetRootNode(docId)

Gets the root node of a document.

new root = JSON_GetRootNode(docId);
if (root == JSON_INVALID_NODE)
{
    print("Document has no root node");
}

Returns: Node ID or JSON_INVALID_NODE (-1).


Parsing

JSON_Parse(const jsonString[], &docId)

Parses a JSON string into a new document.

new docId;
new input[] = "{\"key\":\"value\",\"num\":42}";

if (JSON_Parse(input, docId))
{
    new root = JSON_GetRootNode(docId);
    // ... use the data ...
    JSON_DestroyDocument(docId);
}
else
{
    new errMsg[128];
    JSON_GetErrorMessage(errMsg);
    printf("Parse error: %s", errMsg);
}

Returns: 1 on success, 0 on failure.

Supported JSON features:

  • Objects, arrays, strings, integers, floats, booleans, null
  • Nested structures to JSON_MAX_DEPTH levels
  • Escape sequences in strings: \", \\, \/, \b, \f, \n, \r, \t
  • Negative numbers and decimal floats

JSON_ParseFile(const filename[], &docId)

Reads a file and parses its contents as JSON.

new docId;
if (JSON_ParseFile("scriptfiles/config.json", docId))
{
    new root = JSON_GetRootNode(docId);
    // ...
    JSON_DestroyDocument(docId);
}

Returns: 1 on success, 0 on failure.


Node Information

JSON_GetNodeType(nodeId)

Returns the type constant of a node.

new type = JSON_GetNodeType(nodeId);
if (type == JSON_TYPE_STRING)
{
    // handle string
}

Returns: One of JSON_TYPE_* constants, or -1 if invalid.


JSON_GetNodeTypeName(nodeId, dest[], maxlen = sizeof(dest))

Gets a human-readable type name.

new typeName[16];
JSON_GetNodeTypeName(nodeId, typeName);
printf("Type: %s", typeName);
// Output: "string", "int", "float", "bool", "null", "object", "array", or "unknown"

Returns: 1 on success, 0 on failure.


JSON_GetNodeKey(nodeId, dest[], maxlen = sizeof(dest))

Gets the key of a node (only meaningful for children of objects).

new key[64];
JSON_GetNodeKey(childNode, key);
printf("Key: %s", key);

Returns: 1 on success, 0 on failure.


JSON_GetChildCount(nodeId)

Returns the number of direct children of a node.

new count = JSON_GetChildCount(objectNode);
printf("Object has %d properties", count);

Returns: Child count, or 0 if invalid/no children.


Type Check Functions

All return bool:

JSON_IsNull(nodeId)      // true if JSON_TYPE_NULL
JSON_IsString(nodeId)    // true if JSON_TYPE_STRING
JSON_IsInt(nodeId)       // true if JSON_TYPE_INT
JSON_IsFloat(nodeId)     // true if JSON_TYPE_FLOAT
JSON_IsBool(nodeId)      // true if JSON_TYPE_BOOL
JSON_IsObject(nodeId)    // true if JSON_TYPE_OBJECT
JSON_IsArray(nodeId)     // true if JSON_TYPE_ARRAY
JSON_IsNumber(nodeId)    // true if JSON_TYPE_INT or JSON_TYPE_FLOAT

Example:

if (JSON_IsString(node))
{
    new val[128];
    JSON_GetString(node, val);
    printf("String value: %s", val);
}
else if (JSON_IsInt(node))
{
    new val;
    JSON_GetInt(node, val);
    printf("Int value: %d", val);
}

Value Getters

JSON_GetString(nodeId, dest[], maxlen = sizeof(dest))

Gets the string value of a node.

new value[128];
if (JSON_GetString(node, value))
{
    printf("Value: %s", value);
}

Returns: 1 on success, 0 on failure. Works on JSON_TYPE_STRING nodes.


JSON_GetInt(nodeId, &value)

Gets the integer value of a node.

new value;
if (JSON_GetInt(node, value))
{
    printf("Value: %d", value);
}

Returns: 1 on success, 0 on failure. Works on JSON_TYPE_INT nodes.


JSON_GetFloat(nodeId, &Float:value)

Gets the float value of a node.

new Float:value;
if (JSON_GetFloat(node, value))
{
    printf("Value: %f", value);
}

Returns: 1 on success, 0 on failure. Works on JSON_TYPE_FLOAT nodes.


JSON_GetBool(nodeId, &bool:value)

Gets the boolean value of a node.

new bool:value;
if (JSON_GetBool(node, value))
{
    printf("Value: %s", value ? "true" : "false");
}

Returns: 1 on success, 0 on failure. Works on JSON_TYPE_BOOL nodes.


Navigation

These functions let you traverse the node tree manually.

JSON_GetParent(nodeId)

new parent = JSON_GetParent(childNode);

Returns: Parent node ID, or JSON_INVALID_NODE.


JSON_GetFirstChild(nodeId)

new first = JSON_GetFirstChild(objectNode);

Returns: First child node ID, or JSON_INVALID_NODE.


JSON_GetLastChild(nodeId)

new last = JSON_GetLastChild(objectNode);

Returns: Last child node ID, or JSON_INVALID_NODE.


JSON_GetNextSibling(nodeId)

new next = JSON_GetNextSibling(currentNode);

Returns: Next sibling node ID, or JSON_INVALID_NODE.


JSON_GetPrevSibling(nodeId)

new prev = JSON_GetPrevSibling(currentNode);

Returns: Previous sibling node ID, or JSON_INVALID_NODE.


Object Lookup

JSON_GetObjectChild(objectNode, const key[])

Finds a direct child of an object by key name.

new nameNode = JSON_GetObjectChild(root, "name");
if (nameNode != JSON_INVALID_NODE)
{
    new name[32];
    JSON_GetString(nameNode, name);
}

Returns: Node ID or JSON_INVALID_NODE.


JSON_ObjectHasKey(objectNode, const key[])

Checks if an object contains a specific key.

if (JSON_ObjectHasKey(root, "email"))
{
    // key exists
}

Returns: bool.


Array Access

JSON_GetArrayElement(arrayNode, index)

Gets an array element by zero-based index.

new elem = JSON_GetArrayElement(arrayNode, 0); // first element
if (elem != JSON_INVALID_NODE)
{
    new val;
    JSON_GetInt(elem, val);
}

Returns: Node ID or JSON_INVALID_NODE.


JSON_GetArraySize(arrayNode)

Returns the number of elements in an array.

new size = JSON_GetArraySize(arrayNode);
printf("Array has %d elements", size);

Returns: Element count, or 0.


Path Queries (Dot Notation)

The path query system lets you access deeply nested values using dot notation with bracket syntax for arrays.

Path syntax:

  • "key" - direct child of object
  • "parent.child" - nested object access
  • "array[0]" - array element by index
  • "parent.array[2].name" - combined

JSON_GetByPath(rootNode, const path[])

Retrieves a node by path.

// Given: {"user":{"name":"Ali","scores":[10,20,30]}}
new node = JSON_GetByPath(root, "user.name");
// node points to the "Ali" string node

new scoreNode = JSON_GetByPath(root, "user.scores[1]");
// scoreNode points to the 20 integer node

Returns: Node ID or JSON_INVALID_NODE.


Convenience Path Getters

These combine JSON_GetByPath with the appropriate value getter:

JSON_GetStringByPath(rootNode, const path[], dest[], maxlen = sizeof(dest))

new name[32];
JSON_GetStringByPath(root, "user.name", name);

Returns: 1 on success, 0 on failure.


JSON_GetIntByPath(rootNode, const path[], &value)

new score;
JSON_GetIntByPath(root, "user.scores[0]", score);

Returns: 1 on success, 0 on failure.


JSON_GetFloatByPath(rootNode, const path[], &Float:value)

new Float:x;
JSON_GetFloatByPath(root, "position.x", x);

Returns: 1 on success, 0 on failure.


JSON_GetBoolByPath(rootNode, const path[], &bool:value)

new bool:isAdmin;
JSON_GetBoolByPath(root, "user.admin", isAdmin);

Returns: 1 on success, 0 on failure.


Path Setters

JSON_SetByPath(docId, rootNode, const path[], valueNode)

Sets a value at a given path. Automatically creates intermediate objects if they don't exist.

new strNode = JSON_CreateString(docId, "hello");
JSON_SetByPath(docId, root, "deep.nested.key", strNode);

Returns: 1 on success, 0 on failure.


Convenience Path Setters

JSON_SetStringByPath(docId, rootNode, const path[], const value[])

JSON_SetStringByPath(docId, root, "user.email", "test@example.com");

JSON_SetIntByPath(docId, rootNode, const path[], value)

JSON_SetIntByPath(docId, root, "user.level", 5);

JSON_SetFloatByPath(docId, rootNode, const path[], Float:value)

JSON_SetFloatByPath(docId, root, "player.health", 85.5);

JSON_SetBoolByPath(docId, rootNode, const path[], bool:value)

JSON_SetBoolByPath(docId, root, "player.alive", true);

Node Creation

These functions create new standalone nodes belonging to a document.

JSON_CreateObject(docId)

new obj = JSON_CreateObject(docId);

JSON_CreateArray(docId)

new arr = JSON_CreateArray(docId);

JSON_CreateString(docId, const value[])

new strNode = JSON_CreateString(docId, "hello world");

JSON_CreateInt(docId, value)

new intNode = JSON_CreateInt(docId, 42);

JSON_CreateFloat(docId, Float:value)

new floatNode = JSON_CreateFloat(docId, 3.14);

JSON_CreateBool(docId, bool:value)

new boolNode = JSON_CreateBool(docId, true);

JSON_CreateNull(docId)

new nullNode = JSON_CreateNull(docId);

All return: Node ID on success, JSON_INVALID_NODE on failure.


Object Building

JSON_ObjectSetValue(objectNode, const key[], valueNode)

Sets a key-value pair on an object. If the key already exists, the old value is replaced.

new obj = JSON_CreateObject(docId);
new strNode = JSON_CreateString(docId, "world");
JSON_ObjectSetValue(obj, "hello", strNode);

Returns: 1 on success, 0 on failure.


JSON_ObjectSetString(objectNode, const key[], const value[])

Shorthand to set a string value on an object.

JSON_ObjectSetString(obj, "name", "Ali");

JSON_ObjectSetInt(objectNode, const key[], value)

JSON_ObjectSetInt(obj, "age", 25);

JSON_ObjectSetFloat(objectNode, const key[], Float:value)

JSON_ObjectSetFloat(obj, "score", 99.5);

JSON_ObjectSetBool(objectNode, const key[], bool:value)

JSON_ObjectSetBool(obj, "vip", true);

JSON_ObjectSetNull(objectNode, const key[])

JSON_ObjectSetNull(obj, "deletedAt");

JSON_ObjectRemoveKey(objectNode, const key[])

Removes a key and its value from an object.

JSON_ObjectRemoveKey(obj, "tempData");

Returns: 1 if removed, 0 if key not found.


Array Building

JSON_ArrayAppend(arrayNode, valueNode)

Appends a node to the end of an array.

new arr = JSON_CreateArray(docId);
JSON_ArrayAppend(arr, JSON_CreateInt(docId, 10));
JSON_ArrayAppend(arr, JSON_CreateInt(docId, 20));
JSON_ArrayAppend(arr, JSON_CreateInt(docId, 30));
// arr = [10, 20, 30]

Returns: 1 on success, 0 on failure.


JSON_ArrayAppendString(arrayNode, docId, const value[])

JSON_ArrayAppendString(arr, docId, "hello");

JSON_ArrayAppendInt(arrayNode, docId, value)

JSON_ArrayAppendInt(arr, docId, 42);

JSON_ArrayAppendFloat(arrayNode, docId, Float:value)

JSON_ArrayAppendFloat(arr, docId, 3.14);

JSON_ArrayAppendBool(arrayNode, docId, bool:value)

JSON_ArrayAppendBool(arr, docId, true);

JSON_ArrayInsert(arrayNode, index, valueNode)

Inserts a node at a specific index in an array. Existing elements shift right.

// arr = [10, 30]
JSON_ArrayInsert(arr, 1, JSON_CreateInt(docId, 20));
// arr = [10, 20, 30]

Returns: 1 on success, 0 on failure.


JSON_ArrayRemove(arrayNode, index)

Removes an element at a specific index from an array.

// arr = [10, 20, 30]
JSON_ArrayRemove(arr, 1);
// arr = [10, 30]

Returns: 1 on success, 0 on failure.


Quick Build Helpers

JSON_BuildObject(&docId)

Creates a new document with an empty root object. Returns the root node.

new docId, root;
root = JSON_BuildObject(docId);
JSON_ObjectSetString(root, "hello", "world");

new output[256];
JSON_Serialize(root, output);
// output = {"hello":"world"}

JSON_DestroyDocument(docId);

Returns: Root node ID, or JSON_INVALID_NODE.


JSON_BuildArray(&docId)

Creates a new document with an empty root array. Returns the root node.

new docId, root;
root = JSON_BuildArray(docId);
JSON_ArrayAppendInt(root, docId, 1);
JSON_ArrayAppendInt(root, docId, 2);
JSON_ArrayAppendInt(root, docId, 3);

new output[256];
JSON_Serialize(root, output);
// output = [1,2,3]

JSON_DestroyDocument(docId);

Returns: Root node ID, or JSON_INVALID_NODE.


Serialization

JSON_Serialize(nodeId, dest[], maxlen = sizeof(dest), bool:pretty = false)

Converts a node tree back into a JSON string.

new output[1024];

// Compact
JSON_Serialize(root, output, sizeof(output), false);
// {"name":"Ali","age":25}

// Pretty
JSON_Serialize(root, output, sizeof(output), true);
// {
//   "name": "Ali",
//   "age": 25
// }

Returns: 1 on success, 0 on failure.


JSON_Minify(nodeId, dest[], maxlen = sizeof(dest))

Shorthand for compact serialization.

new output[512];
JSON_Minify(root, output);

JSON_Prettify(nodeId, dest[], maxlen = sizeof(dest))

Shorthand for pretty-printed serialization.

new output[1024];
JSON_Prettify(root, output);

JSON_SerializeToFile(nodeId, const filename[], bool:pretty = true)

Serializes a node and writes it to a file.

JSON_SerializeToFile(root, "scriptfiles/output.json", true);

Returns: 1 on success, 0 on failure.


JSON_SerializeDoc(docId, dest[], maxlen = sizeof(dest), bool:pretty = false)

Serializes the root node of a document.

new output[1024];
JSON_SerializeDoc(docId, output, sizeof(output), true);

JSON_SaveDocument(docId, const filename[], bool:pretty = true)

Saves a document to a file.

JSON_SaveDocument(docId, "scriptfiles/data.json");

Returns: 1 on success, 0 on failure.


Deep Copy / Clone

JSON_CloneNode(docId, sourceNode)

Creates a deep copy of a node and all its children into the specified document.

new cloned = JSON_CloneNode(docId, originalNode);
// cloned is a completely independent copy

Returns: New node ID, or JSON_INVALID_NODE.


Merge

JSON_MergeObjects(docId, targetNode, sourceNode, bool:overwrite = true)

Merges all keys from sourceNode into targetNode. Both must be objects.

// target = {"a": 1, "b": 2}
// source = {"b": 99, "c": 3}

JSON_MergeObjects(docId, target, source, true);
// target = {"a": 1, "b": 99, "c": 3}

JSON_MergeObjects(docId, target, source, false);
// target = {"a": 1, "b": 2, "c": 3}  (b not overwritten)

If both the existing and incoming values for a key are objects, they are merged recursively.

Returns: 1 on success, 0 on failure.


Comparison

JSON_Equal(nodeA, nodeB)

Recursively compares two nodes for structural and value equality.

if (JSON_Equal(nodeA, nodeB))
{
    print("Nodes are identical");
}

Returns: bool. Compares types, values, keys, children count, and all descendants.


Search

JSON_FindByKey(nodeId, const key[], startFrom = JSON_INVALID_NODE)

Recursively searches the tree for a node with the given key. Returns the first match.

// Searches entire tree under root for any node with key "email"
new found = JSON_FindByKey(root, "email");
if (found != JSON_INVALID_NODE)
{
    new email[128];
    JSON_GetString(found, email);
}

Returns: Node ID or JSON_INVALID_NODE.


JSON_FindByStringValue(nodeId, const value[])

Recursively searches for a string node with the given value.

new found = JSON_FindByStringValue(root, "admin");
if (found != JSON_INVALID_NODE)
{
    new key[64];
    JSON_GetNodeKey(found, key);
    printf("Found 'admin' at key: %s", key);
}

Returns: Node ID or JSON_INVALID_NODE.


JSON_FindByIntValue(nodeId, value)

Recursively searches for an integer node with the given value.

new found = JSON_FindByIntValue(root, 42);

Returns: Node ID or JSON_INVALID_NODE.


Iteration

Iterator Functions

Manual iteration using JSON_IterateObject and JSON_IterateArray:

// Iterate object properties
new child = JSON_IterateObject(objectNode, JSON_INVALID_NODE);
while (child != JSON_INVALID_NODE)
{
    new key[64];
    JSON_GetNodeKey(child, key);
    printf("Key: %s", key);

    child = JSON_IterateObject(objectNode, child);
}


// Iterate array elements
new elem = JSON_IterateArray(arrayNode, JSON_INVALID_NODE);
while (elem != JSON_INVALID_NODE)
{
    new val;
    JSON_GetInt(elem, val);
    printf("Value: %d", val);

    elem = JSON_IterateArray(arrayNode, elem);
}

Foreach Macros

Cleaner syntax using macros:

// Iterate object
JSON_foreach_object(child, objectNode)
{
    new key[64], val[128];
    JSON_GetNodeKey(child, key);
    JSON_GetString(child, val);
    printf("%s = %s", key, val);
}

// Iterate array
JSON_foreach_array(elem, arrayNode)
{
    new val;
    JSON_GetInt(elem, val);
    printf("Element: %d", val);
}

Object Keys Extraction

JSON_GetObjectKeys(objectNode, keys[][], maxKeys, maxKeyLen = JSON_MAX_KEY_LEN)

Extracts all keys from an object into a 2D string array.

new keys[32][64];
new count = JSON_GetObjectKeys(objectNode, keys, 32, 64);

for (new i = 0; i < count; i++)
{
    printf("Key %d: %s", i, keys[i]);
}

Returns: Number of keys extracted.


Path Manipulation

JSON_EnsurePath(docId, rootNode, const path[])

Ensures all intermediate objects exist along a dot-notation path. Creates them if missing.

// root = {}
new deepNode = JSON_EnsurePath(docId, root, "a.b.c");
// root = {"a":{"b":{"c":{}}}}
// deepNode points

Returns: Node ID of the final node in the path, or JSON_INVALID_NODE.


JSON_RemoveByPath(rootNode, const path[])

Removes a node at the specified path.

// root = {"user":{"name":"Ali","temp":"data"}}
JSON_RemoveByPath(root, "user.temp");
// root = {"user":{"name":"Ali"}}

Returns: 1 on success, 0 if path not found.


Validation

JSON_Validate(const jsonString[])

Validates whether a string is valid JSON without fully parsing it.

if (JSON_Validate("{\"key\":\"value\"}"))
{
    print("Valid JSON");
}
else
{
    print("Invalid JSON");
}

Returns: bool.


Statistics and Debug

JSON_CountNodes(nodeId)

Recursively counts all nodes in a subtree (including the node itself).

new total = JSON_CountNodes(root);
printf("Total nodes: %d", total);

JSON_GetMaxDepth(nodeId)

Returns the maximum nesting depth of a subtree.

new depth = JSON_GetMaxDepth(root);
printf("Max depth: %d", depth);

JSON_GetFreeNodeCount()

Returns how many node slots are still available.

new free = JSON_GetFreeNodeCount();
printf("Free nodes: %d / %d", free, JSON_MAX_NODES);

JSON_DebugPrintNode(nodeId, depth = 0)

Prints a debug representation of a node and all its children to the server console. Useful for development.

JSON_DebugPrintNode(root);

Output example:

[OBJECT] (root) [STRING] name = "Ali" [INT] age = 25 [ARRAY] scores [INT] = 10 [INT] = 20 [INT] = 30


Type Conversion

JSON_NodeToString(nodeId, dest[], maxlen = sizeof(dest))

Converts any node value to its string representation regardless of type.

new str[64];

// Integer node with value 42
JSON_NodeToString(intNode, str);
// str = "42"

// Boolean node with value true
JSON_NodeToString(boolNode, str);
// str = "true"

// Null node
JSON_NodeToString(nullNode, str);
// str = "null"

Returns: 1 on success, 0 on failure.


JSON_NodeToInt(nodeId, &value)

Attempts to convert any node value to an integer.

new val;
// String node "123"
JSON_NodeToInt(stringNode, val);
// val = 123

// Bool node true
JSON_NodeToInt(boolNode, val);
// val = 1

Returns: 1 on success, 0 on failure.


JSON_NodeToFloat(nodeId, &Float:value)

Attempts to convert any node value to a float.

new Float:val;
// String node "3.14"
JSON_NodeToFloat(stringNode, val);
// val = 3.14

// Int node 42
JSON_NodeToFloat(intNode, val);
// val = 42.0

Returns: 1 on success, 0 on failure.


Cleanup

JSON_Cleanup()

Frees all documents and resets all internal state. Use with caution - destroys everything.

JSON_Cleanup();

Returns: 1 always.


Error Handling

JSON_GetErrorMessage(dest[], maxlen = sizeof(dest))

Retrieves the last error message from a failed parse operation.

new docId;
if (!JSON_Parse(badInput, docId))
{
    new err[128];
    JSON_GetErrorMessage(err);
    printf("Error: %s", err);
}

Common error messages:

  • "Unexpected character at position X"
  • "Maximum nesting depth exceeded"
  • "Unterminated string"
  • "No free nodes available"
  • "No free document slots"
  • "Invalid number format"

Macros

Macro Description
JSON_foreach_object(var, node) Iterates over all children of an object node
JSON_foreach_array(var, node) Iterates over all elements of an array node

Usage:

JSON_foreach_object(child, myObject)
{
    // child is the current child node ID
}

JSON_foreach_array(elem, myArray)
{
    // elem is the current element node ID
}

Constants

Constant Value Description
JSON_INVALID_NODE -1 Returned when a node operation fails
JSON_INVALID_DOCUMENT -1 Returned when a document operation fails
JSON_TYPE_NULL 0 Null type
JSON_TYPE_STRING 1 String type
JSON_TYPE_INT 2 Integer type
JSON_TYPE_FLOAT 3 Float type
JSON_TYPE_BOOL 4 Boolean type
JSON_TYPE_OBJECT 5 Object type
JSON_TYPE_ARRAY 6 Array type

Complete Examples

Example 1: Parse and Read

#include <a_samp>
#include <json_parser>

public OnFilterScriptInit()
{
    new docId;
    new jsonStr[] = "{\
        \"server\": \"My Server\",\
        \"maxPlayers\": 100,\
        \"gamemode\": \"freeroam\",\
        \"weather\": 10,\
        \"gravity\": 0.008,\
        \"website\": \"example.com\",\
        \"active\": true\
    }";

    if (JSON_Parse(jsonStr, docId))
    {
        new root = JSON_GetRootNode(docId);

        new serverName[64];
        JSON_GetStringByPath(root, "server", serverName);
        printf("Server: %s", serverName);

        new maxPlayers;
        JSON_GetIntByPath(root, "maxPlayers", maxPlayers);
        printf("Max Players: %d", maxPlayers);

        new Float:gravity;
        JSON_GetFloatByPath(root, "gravity", gravity);
        printf("Gravity: %f", gravity);

        new bool:active;
        JSON_GetBoolByPath(root, "active", active);
        printf("Active: %s", active ? "yes" : "no");

        JSON_DestroyDocument(docId);
    }
    return 1;
}

Example 2: Build from Scratch

#include <a_samp>
#include <json_parser>

public OnFilterScriptInit()
{
    new docId, root;
    root = JSON_BuildObject(docId);

    JSON_ObjectSetString(root, "name", "Ali");
    JSON_ObjectSetInt(root, "age", 28);
    JSON_ObjectSetFloat(root, "balance", 1500.75);
    JSON_ObjectSetBool(root, "vip", true);
    JSON_ObjectSetNull(root, "clan");

    // Add nested object
    new address = JSON_CreateObject(docId);
    JSON_ObjectSetString(address, "city", "Tehran");
    JSON_ObjectSetString(address, "country", "Iran");
    JSON_ObjectSetValue(root, "address", address);

    // Add array
    new weapons = JSON_CreateArray(docId);
    JSON_ArrayAppendInt(weapons, docId, 24);
    JSON_ArrayAppendInt(weapons, docId, 31);
    JSON_ArrayAppendInt(weapons, docId, 34);
    JSON_ObjectSetValue(root, "weapons", weapons);

    new output[1024];
    JSON_Prettify(root, output);
    print(output);

    JSON_DestroyDocument(docId);
    return 1;
}

Output:

{
  "name": "Ali",
  "age": 28,
  "balance": 1500.75,
  "vip": true,
  "clan": null,
  "address": {
    "city": "Tehran",
    "country": "Iran"
  },
  "weapons": [24, 31, 34]
}

Example 3: File I/O

#include <a_samp>
#include <json_parser>

SaveConfig()
{
    new docId, root;
    root = JSON_BuildObject(docId);

    JSON_ObjectSetString(root, "hostname", "My SA-MP Server");
    JSON_ObjectSetInt(root, "port", 7777);
    JSON_ObjectSetInt(root, "maxPlayers", 50);
    JSON_ObjectSetBool(root, "announce", true);

    JSON_SaveDocument(docId, "scriptfiles/server_config.json", true);
    JSON_DestroyDocument(docId);
    print("Config saved.");
}

LoadConfig()
{
    new docId;
    if (JSON_ParseFile("scriptfiles/server_config.json", docId))
    {
        new root = JSON_GetRootNode(docId);

        new hostname[64];
        JSON_GetStringByPath(root, "hostname", hostname);
        printf("Hostname: %s", hostname);

        new port;
        JSON_GetIntByPath(root, "port", port);
        printf("Port: %d", port);

        JSON_DestroyDocument(docId);
    }
    else
    {
        new err[128];
        JSON_GetErrorMessage(err);
        printf("Failed to load config: %s", err);
    }
}

Example 4: Modify Existing JSON

#include <a_samp>
#include <json_parser>

public OnFilterScriptInit()
{
    new docId;
    new input[] = "{\"player\":\"Ali\",\"score\":100,\"level\":5}";

    if (JSON_Parse(input, docId))
    {
        new root = JSON_GetRootNode(docId);

        // Update score
        JSON_SetIntByPath(docId, root, "player.score", 250);

        // Update level
        JSON_SetIntByPath(docId, root, "level", 6);

        // Add new field
        JSON_SetStringByPath(docId, root, "rank", "Gold");

        // Remove a field
        JSON_RemoveByPath(root, "player");

        new output[512];
        JSON_Prettify(root, output);
        print(output);

        JSON_DestroyDocument(docId);
    }
    return 1;
}

Example 5: Deep Nesting and Path Queries

#include <a_samp>
#include <json_parser>

public OnFilterScriptInit()
{
    new docId;
    new input[] = "{\
        \"game\":{\
            \"world\":{\
                \"weather\":10,\
                \"time\":\"12:00\",\
                \"spawns\":[\
                    {\"name\":\"LS\",\"x\":1500.0,\"y\":-1700.0,\"z\":13.5},\
                    {\"name\":\"SF\",\"x\":-1950.0,\"y\":300.0,\"z\":35.0}\
                ]\
            }\
        }\
    }";

    if (JSON_Parse(input, docId))
    {
        new root = JSON_GetRootNode(docId);

        new weather;
        JSON_GetIntByPath(root, "game.world.weather", weather);
        printf("Weather: %d", weather);

        new spawnName[32];
        JSON_GetStringByPath(root, "game.world.spawns[0].name", spawnName);
        printf("First spawn: %s", spawnName);

        new Float:x;
        JSON_GetFloatByPath(root, "game.world.spawns[1].x", x);
        printf("SF x: %f", x);

        JSON_DestroyDocument(docId);
    }
    return 1;
}

Example 6: Array Manipulation

#include <a_samp>
#include <json_parser>

public OnFilterScriptInit()
{
    new docId, root;
    root = JSON_BuildObject(docId);

    // Create array
    new scores = JSON_CreateArray(docId);
    JSON_ArrayAppendInt(scores, docId, 10);
    JSON_ArrayAppendInt(scores, docId, 20);
    JSON_ArrayAppendInt(scores, docId, 30);
    JSON_ArrayAppendInt(scores, docId, 40);
    JSON_ObjectSetValue(root, "scores", scores);

    printf("Size: %d", JSON_GetArraySize(scores)); // 4

    // Insert at index 2
    JSON_ArrayInsert(scores, 2, JSON_CreateInt(docId, 25));
    // [10, 20, 25, 30, 40]

    // Remove index 0
    JSON_ArrayRemove(scores, 0);
    // [20, 25, 30, 40]

    // Read element
    new val;
    new elem = JSON_GetArrayElement(scores, 1);
    JSON_GetInt(elem, val);
    printf("scores[1] = %d", val); // 25

    // Iterate
    printf("All scores:");
    JSON_foreach_array(e, scores)
    {
        new v;
        JSON_GetInt(e, v);
        printf("  %d", v);
    }

    new output[256];
    JSON_Serialize(root, output);
    print(output);

    JSON_DestroyDocument(docId);
    return 1;
}

Example 7: Merge Two Objects

#include <a_samp>
#include <json_parser>

public OnFilterScriptInit()
{
    new docId;

    // Base config
    new baseStr[] = "{\"hostname\":\"Server\",\"maxPlayers\":50,\"weather\":1}";
    JSON_Parse(baseStr, docId);
    new base = JSON_GetRootNode(docId);

    // Override config
    new overrideDocId;
    new overrideStr[] = "{\"maxPlayers\":100,\"password\":\"secret\",\"weather\":10}";
    JSON_Parse(overrideStr, overrideDocId);
    new override = JSON_GetRootNode(overrideDocId);

    // Merge with overwrite
    JSON_MergeObjects(docId, base, override, true);

    new output[512];
    JSON_Prettify(base, output);
    print(output);
    // hostname = "Server", maxPlayers = 100, weather = 10, password = "secret"

    JSON_DestroyDocument(docId);
    JSON_DestroyDocument(overrideDocId);
    return 1;
}

Example 8: Clone and Compare

#include <a_samp>
#include <json_parser>

public OnFilterScriptInit()
{
    new docId;
    new input[] = "{\"a\":1,\"b\":{\"c\":2,\"d\":[3,4,5]}}";
    JSON_Parse(input, docId);
    new root = JSON_GetRootNode(docId);

    // Clone
    new cloned = JSON_CloneNode(docId, root);

    // Compare
    if (JSON_Equal(root, cloned))
    {
        print("Original and clone are equal.");
    }

    // Modify clone
    JSON_SetIntByPath(docId, cloned, "b.c", 999);

    if (!JSON_Equal(root, cloned))
    {
        print("After modification, they differ.");
    }

    new out1[256], out2[256];
    JSON_Serialize(root, out1);
    JSON_Serialize(cloned, out2);
    printf("Original: %s", out1);
    printf("Clone:    %s", out2);

    JSON_DestroyDocument(docId);
    return 1;
}

Example 9: Search Functions

#include <a_samp>
#include <json_parser>

public OnFilterScriptInit()
{
    new docId;
    new input[] = "{\
        \"users\":[\
            {\"name\":\"Ali\",\"role\":\"admin\"},\
            {\"name\":\"Sara\",\"role\":\"moderator\"},\
            {\"name\":\"Reza\",\"role\":\"admin\"}\
        ]\
    }";

    if (JSON_Parse(input, docId))
    {
        new root = JSON_GetRootNode(docId);

        // Find first node with key "role"
        new found = JSON_FindByKey(root, "role");
        if (found != JSON_INVALID_NODE)
        {
            new val[32];
            JSON_GetString(found, val);
            printf("First role found: %s", val);
        }

        // Find node with string value "moderator"
        new modNode = JSON_FindByStringValue(root, "moderator");
        if (modNode != JSON_INVALID_NODE)
        {
            new parent = JSON_GetParent(modNode);
            new name[32];
            new nameNode = JSON_GetObjectChild(parent, "name");
            JSON_GetString(nameNode, name);
            printf("Moderator: %s", name);
        }

        JSON_DestroyDocument(docId);
    }
    return 1;
}

Example 10: Iteration

#include <a_samp>
#include <json_parser>

public OnFilterScriptInit()
{
    new docId;
    new input[] = "{\
        \"name\":\"Ali\",\
        \"age\":25,\
        \"vip\":true,\
        \"scores\":[90,85,92]\
    }";

    if (JSON_Parse(input, docId))
    {
        new root = JSON_GetRootNode(docId);

        // Iterate all object keys
        print("=== Object Properties ===");
        JSON_foreach_object(child, root)
        {
            new key[64], typeName[16];
            JSON_GetNodeKey(child, key);
            JSON_GetNodeTypeName(child, typeName);
            printf("  %s (%s)", key, typeName);
        }

        // Iterate array
        new scoresNode = JSON_GetObjectChild(root, "scores");
        print("=== Scores ===");
        new idx = 0;
        JSON_foreach_array(elem, scoresNode)
        {
            new val;
            JSON_GetInt(elem, val);
            printf("  [%d] = %d", idx, val);
            idx++;
        }

        // Get all keys as array
        new keys[16][64];
        new keyCount = JSON_GetObjectKeys(root, keys, 16, 64);
        print("=== Keys ===");
        for (new i = 0; i < keyCount; i++)
        {
            printf("  Key %d: %s", i, keys[i]);
        }

        JSON_DestroyDocument(docId);
    }
    return 1;
}

Example 11: Player Save/Load System

#include <a_samp>
#include <json_parser>

enum PlayerData
{
    pName[MAX_PLAYER_NAME],
    pScore,
    Float:pHealth,
    Float:pPosX,
    Float:pPosY,
    Float:pPosZ,
    Float:pAngle,
    pMoney,
    pSkin,
    bool:pVIP
};

new gPlayerData[MAX_PLAYERS][PlayerData];

GetPlayerFile(playerid, dest[], maxlen)
{
    new name[MAX_PLAYER_NAME];
    GetPlayerName(playerid, name, sizeof(name));
    format(dest, maxlen, "scriptfiles/players/%s.json", name);
}

SavePlayer(playerid)
{
    new docId, root;
    root = JSON_BuildObject(docId);

    GetPlayerName(playerid, gPlayerData[playerid][pName], MAX_PLAYER_NAME);

    JSON_ObjectSetString(root, "name", gPlayerData[playerid][pName]);
    JSON_ObjectSetInt(root, "score", GetPlayerScore(playerid));
    JSON_ObjectSetInt(root, "money", GetPlayerMoney(playerid));
    JSON_ObjectSetInt(root, "skin", GetPlayerSkin(playerid));
    JSON_ObjectSetBool(root, "vip", gPlayerData[playerid][pVIP]);

    // Position
    new Float:x, Float:y, Float:z, Float:a;
    GetPlayerPos(playerid, x, y, z);
    GetPlayerFacingAngle(playerid, a);

    new pos = JSON_CreateObject(docId);
    JSON_ObjectSetFloat(pos, "x", x);
    JSON_ObjectSetFloat(pos, "y", y);
    JSON_ObjectSetFloat(pos, "z", z);
    JSON_ObjectSetFloat(pos, "angle", a);
    JSON_ObjectSetValue(root, "position", pos);

    new Float:hp;
    GetPlayerHealth(playerid, hp);
    JSON_ObjectSetFloat(root, "health", hp);

    // Weapons array
    new weapons = JSON_CreateArray(docId);
    for (new slot = 0; slot < 13; slot++)
    {
        new weaponId, ammo;
        GetPlayerWeaponData(playerid, slot, weaponId, ammo);
        if (weaponId != 0)
        {
            new w = JSON_CreateObject(docId);
            JSON_ObjectSetInt(w, "id", weaponId);
            JSON_ObjectSetInt(w, "ammo", ammo);
            JSON_ArrayAppend(weapons, w);
        }
    }
    JSON_ObjectSetValue(root, "weapons", weapons);

    new file[128];
    GetPlayerFile(playerid, file, sizeof(file));
    JSON_SaveDocument(docId, file, true);
    JSON_DestroyDocument(docId);

    printf("[SAVE] Player %s saved.", gPlayerData[playerid][pName]);
}

LoadPlayer(playerid)
{
    new file[128];
    GetPlayerFile(playerid, file, sizeof(file));

    new docId;
    if (!JSON_ParseFile(file, docId))
    {
        printf("[LOAD] No save file for player %d", playerid);
        return 0;
    }

    new root = JSON_GetRootNode(docId);

    JSON_GetStringByPath(root, "name", gPlayerData[playerid][pName]);

    new score;
    JSON_GetIntByPath(root, "score", score);
    SetPlayerScore(playerid, score);

    new money;
    JSON_GetIntByPath(root, "money", money);
    GivePlayerMoney(playerid, money);

    new skin;
    JSON_GetIntByPath(root, "skin", skin);
    SetPlayerSkin(playerid, skin);

    JSON_GetBoolByPath(root, "vip", gPlayerData[playerid][pVIP]);

    new Float:hp;
    JSON_GetFloatByPath(root, "health", hp);
    SetPlayerHealth(playerid, hp);

    new Float:x, Float:y, Float:z, Float:a;
    JSON_GetFloatByPath(root, "position.x", x);
    JSON_GetFloatByPath(root, "position.y", y);
    JSON_GetFloatByPath(root, "position.z", z);
    JSON_GetFloatByPath(root, "position.angle", a);
    SetPlayerPos(playerid, x, y, z);
    SetPlayerFacingAngle(playerid, a);

    // Load weapons
    new weaponsNode = JSON_GetByPath(root, "weapons");
    if (weaponsNode != JSON_INVALID_NODE)
    {
        new size = JSON_GetArraySize(weaponsNode);
        for (new i = 0; i < size; i++)
        {
            new w = JSON_GetArrayElement(weaponsNode, i);
            new weaponId, ammo;
            JSON_GetIntByPath(w, "id", weaponId);
            JSON_GetIntByPath(w, "ammo", ammo);
            GivePlayerWeapon(playerid, weaponId, ammo);
        }
    }

    JSON_DestroyDocument(docId);
    printf("[LOAD] Player %s loaded.", gPlayerData[playerid][pName]);
    return 1;
}

Example 12: Server Configuration Loader

#include <a_samp>
#include <json_parser>

new g_ServerName[64];
new g_MaxPlayers;
new g_Website[128];
new bool:g_Announce;
new Float:g_Gravity;
new g_Weather;

LoadServerConfig()
{
    new docId;
    if (!JSON_ParseFile("scriptfiles/server.json", docId))
    {
        print("[CONFIG] server.json not found, creating defaults...");
        CreateDefaultConfig();
        return;
    }

    new root = JSON_GetRootNode(docId);

    JSON_GetStringByPath(root, "hostname", g_ServerName, sizeof(g_ServerName));
    JSON_GetIntByPath(root, "maxPlayers", g_MaxPlayers);
    JSON_GetStringByPath(root, "website", g_Website, sizeof(g_Website));
    JSON_GetBoolByPath(root, "announce", g_Announce);
    JSON_GetFloatByPath(root, "gravity", g_Gravity);
    JSON_GetIntByPath(root, "weather", g_Weather);

    // Apply settings
    SetGameModeText(g_ServerName);
    SetWeather(g_Weather);
    SetGravity(g_Gravity);

    printf("[CONFIG] Loaded: %s (max: %d)", g_ServerName, g_MaxPlayers);

    JSON_DestroyDocument(docId);
}

CreateDefaultConfig()
{
    new docId, root;
    root = JSON_BuildObject(docId);

    JSON_ObjectSetString(root, "hostname", "SA-MP Server");
    JSON_ObjectSetInt(root, "maxPlayers", 50);
    JSON_ObjectSetString(root, "website", "sa-mp.com");
    JSON_ObjectSetBool(root, "announce", true);
    JSON_ObjectSetFloat(root, "gravity", 0.008);
    JSON_ObjectSetInt(root, "weather", 1);

    JSON_SaveDocument(docId, "scriptfiles/server.json", true);
    JSON_DestroyDocument(docId);

    // Reload
    LoadServerConfig();
}

Example 13: Validation and Error Handling

#include <a_samp>
#include <json_parser>

TestValidation()
{
    // Valid JSON
    new valid[] = "{\"key\":\"value\"}";
    printf("Valid: %s", JSON_Validate(valid) ? "yes" : "no"); // yes

    // Invalid JSON - missing closing brace
    new invalid1[] = "{\"key\":\"value\"";
    printf("Valid: %s", JSON_Validate(invalid1) ? "yes" : "no"); // no

    // Invalid JSON - trailing comma
    new invalid2[] = "{\"a\":1,}";
    printf("Valid: %s", JSON_Validate(invalid2) ? "yes" : "no"); // no

    // Parse with error handling
    new docId;
    new bad[] = "{broken json here}";
    if (!JSON_Parse(bad, docId))
    {
        new err[128];
        JSON_GetErrorMessage(err);
        printf("Parse error: %s", err);
    }

    // Safe path access - returns 0 if path doesn't exist
    new goodJson[] = "{\"a\":{\"b\":1}}";
    if (JSON_Parse(goodJson, docId))
    {
        new root = JSON_GetRootNode(docId);

        new val;
        if (JSON_GetIntByPath(root, "a.b", val))
        {
            printf("a.b = %d", val); // 1
        }

        // Non-existent path
        if (!JSON_GetIntByPath(root, "a.x.y.z", val))
        {
            print("Path a.x.y.z does not exist");
        }

        // Check before access
        if (JSON_ObjectHasKey(root, "a"))
        {
            print("Key 'a' exists");
        }

        // Node stats
        printf("Total nodes: %d", JSON_CountNodes(root));
        printf("Max depth: %d", JSON_GetMaxDepth(root));
        printf("Free nodes: %d", JSON_GetFreeNodeCount());

        JSON_DestroyDocument(docId);
    }
}

Performance Tips

  1. Destroy documents as soon as you're done with them. Nodes are a shared global resource.

  2. Use path queries (JSON_GetIntByPath, etc.) for quick one-off reads. For iterating many children, use JSON_foreach_object / JSON_foreach_array instead of repeated path lookups.

  3. Increase JSON_MAX_NODES if you work with large JSON files. Monitor usage with JSON_GetFreeNodeCount().

  4. Avoid parsing the same file repeatedly. Parse once, store the values you need, then destroy the document.

  5. Use JSON_Minify for network transmission or compact storage. Use JSON_Prettify only for human-readable output or debugging.

  6. For large arrays, JSON_GetArrayElement is $O(n)$ since it traverses the linked list. If you need sequential access, iterate with JSON_foreach_array instead.

  7. JSON_MergeObjects with deeply nested structures creates clones of source nodes. Be mindful of node consumption.


Limitations

Limitation Detail
Max nodes JSON_MAX_NODES (default 2048) shared across all documents
Max key length JSON_MAX_KEY_LEN (default 64 characters)
Max value length JSON_MAX_VALUE_LEN (default 256 characters)
Max nesting depth JSON_MAX_DEPTH (default 32 levels)
Max documents JSON_MAX_DOCUMENTS (default 16 simultaneous)
Max input string JSON_MAX_STRING_LEN (default 4096 characters)
Serialization buffer JSON_SERIALIZE_BUFFER (default 4096 characters)
Unicode \uXXXX escape sequences are not fully supported
Number precision Floats use Pawn's native Float: (32-bit single precision)
Integer range Pawn's 32-bit signed integer range: $-2{,}147{,}483{,}648$ to $2{,}147{,}483{,}647$
Thread safety Not applicable (Pawn is single-threaded)
Array access $O(n)$ for index-based access (linked list internally)

Changelog

v0.1.0 - Initial Prototype

  • Basic JSON tokenizer and lexer
  • Support for parsing flat JSON objects (no nesting)
  • JSON_Parse() and JSON_GetString() initial implementation
  • Internal node pool with fixed JSON_MAX_NODES
  • Basic error reporting on malformed input

v0.2.0 - Nested Structures

  • Added support for nested objects
  • Added support for arrays (flat)
  • JSON_GetByPath() introduced with simple dot notation ("a.b.c")
  • JSON_GetInt(), JSON_GetFloat(), JSON_GetBool() added
  • Fixed tokenizer crash on trailing commas

v0.3.0 - Array Access & Navigation

  • Array index access in paths ("items[0].name")
  • JSON_GetArrayElement() and JSON_GetArraySize()
  • Tree navigation: JSON_GetParent(), JSON_GetFirstChild(), JSON_GetNextSibling()
  • JSON_GetLastChild() and JSON_GetPrevSibling()
  • JSON_GetChildCount() added

v0.4.0 - Node Creation & Building

  • JSON_CreateDocument() and JSON_DestroyDocument()
  • Node creation functions: JSON_CreateObject(), JSON_CreateArray(), JSON_CreateString(), JSON_CreateInt(), JSON_CreateFloat(), JSON_CreateBool(), JSON_CreateNull()
  • JSON_ObjectSetValue() for attaching nodes to objects
  • JSON_ArrayAppend() for pushing nodes into arrays
  • JSON_GetRootNode() added

v0.5.0 - Serialization

  • JSON_Serialize() with compact output
  • Pretty-print support via pretty parameter (indentation with spaces)
  • JSON_Minify() and JSON_Prettify() shorthand functions
  • Proper escaping of special characters in serialized output (\", \\, \n, \t, \r)

v0.6.0 - File I/O

  • JSON_ParseFile() - load and parse JSON from file
  • JSON_SerializeToFile() - write serialized JSON to file
  • JSON_SaveDocument() and JSON_SerializeDoc() helpers
  • Fixed buffer overflow on large file reads

v0.7.0 - Object Helpers

  • JSON_ObjectSetString(), JSON_ObjectSetInt(), JSON_ObjectSetFloat(), JSON_ObjectSetBool(), JSON_ObjectSetNull() convenience functions
  • JSON_ObjectRemoveKey() with proper sibling re-linking
  • JSON_ObjectHasKey() boolean check
  • JSON_GetObjectChild() direct key lookup

v0.8.0 - Array Manipulation

  • JSON_ArrayAppendString(), JSON_ArrayAppendInt(), JSON_ArrayAppendFloat(), JSON_ArrayAppendBool()
  • JSON_ArrayInsert() - insert at specific index
  • JSON_ArrayRemove() - remove by index with re-linking
  • Fixed off-by-one on array index resolution in paths

v0.9.0 - Path Setters

  • JSON_SetByPath() - set arbitrary node by dot path
  • JSON_SetStringByPath(), JSON_SetIntByPath(), JSON_SetFloatByPath(), JSON_SetBoolByPath()
  • JSON_EnsurePath() - auto-create intermediate objects/arrays along a path
  • JSON_RemoveByPath() - delete node at path

v1.0.0 - Stable Release

  • JSON_Init() and JSON_Cleanup() for global state management
  • JSON_Validate() - lightweight syntax check without full parse
  • JSON_GetLastError() and JSON_GetErrorMessage() with detailed error codes
  • JSON_GetNodeType(), JSON_GetNodeTypeName(), JSON_GetNodeKey()
  • Type check functions: JSON_IsNull(), JSON_IsString(), JSON_IsInt(), JSON_IsFloat(), JSON_IsBool(), JSON_IsObject(), JSON_IsArray(), JSON_IsNumber()
  • JSON_BuildObject() and JSON_BuildArray() quick-build helpers

v1.1.0 - Clone & Merge

  • JSON_CloneNode() - deep recursive copy of any subtree
  • JSON_MergeObjects() with overwrite flag for conflict resolution
  • Fixed clone not preserving key names on nested objects

v1.2.0 - Comparison & Search

  • JSON_Equal() - deep recursive equality check (type + value + structure)
  • JSON_FindByKey() - recursive search by key name
  • JSON_FindByStringValue() and JSON_FindByIntValue()
  • Fixed equality check failing on arrays with identical content but different node IDs

v1.3.0 - Iteration

  • JSON_IterateObject() and JSON_IterateArray() manual iteration functions
  • JSON_foreach_object macro for clean object property loops
  • JSON_foreach_array macro for clean array element loops
  • JSON_GetObjectKeys() - extract all keys into a string array

v1.4.0 - Type Conversion & Stats

  • JSON_NodeToString() - convert any node value to string representation
  • JSON_NodeToInt() - coerce string/float/bool to integer
  • JSON_NodeToFloat() - coerce string/int/bool to float
  • JSON_CountNodes() - count all nodes in a subtree
  • JSON_GetMaxDepth() - measure deepest nesting level
  • JSON_GetFreeNodeCount() - monitor available node pool slots

v1.5.0 - Debug & Polish

  • JSON_DebugPrintNode() - recursive tree dump to server console
  • Improved error messages with line/column context where possible
  • Minor memory optimizations in node allocation
  • Fixed pretty-print adding trailing newline after root close

v2.0.0 - Major Refactor

  • Internal node storage restructured for better cache locality
  • Document isolation improved - nodes no longer leak across documents
  • Path parser rewritten to handle edge cases ("a[0][1].b", empty keys)
  • JSON_MAX_DEPTH now enforced strictly during both parse and build
  • Escape sequence handling expanded (proper \b, \f support)
  • Numeric parser rewritten - handles leading zeros, negative floats, scientific notation edge cases
  • All functions now return consistent error codes
  • Breaking: JSON_Init() must be called before any operation (previously optional)
  • Breaking: JSON_ArrayAppendString/Int/Float/Bool now require docId parameter

v2.1.0 - Robustness Pass

  • Added bounds checking on all path operations
  • JSON_Validate() now catches unbalanced brackets and mismatched quotes
  • Fixed crash when serializing deeply nested structures near JSON_MAX_DEPTH
  • Fixed JSON_MergeObjects() not handling nested object merge recursively
  • Improved JSON_RemoveByPath() to clean up orphaned nodes

v2.1.1 - Hotfix

  • Fixed JSON_ParseFile() failing on files with BOM (byte order mark)
  • Fixed JSON_SetIntByPath() not creating parent array when path contains index

v2.1.2 - Hotfix

  • Fixed JSON_ArrayInsert() corrupting sibling chain when inserting at index 0
  • Fixed JSON_GetObjectKeys() returning stale keys after JSON_ObjectRemoveKey()

v2.1.3 - Hotfix

  • Fixed JSON_CloneNode() not copying null-type nodes
  • Fixed JSON_Prettify() extra indentation on empty arrays [] and objects {}
  • Fixed JSON_Equal() returning true for objects with same keys but different order when values differ

v2.1.4 - Current

  • Fixed JSON_Serialize() buffer overrun on strings containing many escape sequences
  • Fixed JSON_FindByKey() not searching inside arrays
  • JSON_GetFreeNodeCount() now accounts for destroyed document slots
  • Minor performance improvement in JSON_GetByPath() for shallow paths
  • Made the include "prettier" and got better documentation using this Claude guy

License

This library is provided as-is for use in SA-MP Pawn scripts. Feel free to use, modify, and distribute.


About

Pure Pawn JSON Parser for SA-MP/open.mp servers | Full-featured JSON parsing, building, and serialization WITHOUT any plugins

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages