A comprehensive, pure-Pawn JSON parser, builder, serializer, and query engine for SA-MP/open.mp servers. No plugins required.
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.
- Installation
- Configuration
- Quick Start
- Function Quick Refrence
- Core Concepts
- API Reference
- Initialization
- Document Management
- Parsing
- Node Information
- Value Getters
- Navigation
- Object Lookup
- Array Access
- Path Queries (Dot Notation)
- Path Setters
- Node Creation
- Object Building
- Array Building
- Quick Build Helpers
- Serialization
- Deep Copy / Clone
- Merge
- Comparison
- Search
- Iteration
- Object Keys Extraction
- Path Manipulation
- Validation
- Statistics and Debug
- Type Conversion
- Cleanup
- Error Handling
- Macros
- Constants
- Complete Examples
- Example 1: Parse and Read
- Example 2: Build from Scratch
- Example 3: File I/O
- Example 4: Modify Existing JSON
- Example 5: Deep Nesting and Path Queries
- Example 6: Array Manipulation
- Example 7: Merge Two Objects
- Example 8: Clone and Compare
- Example 9: Search Functions
- Example 10: Iteration
- Example 11: Player Save/Load System
- Example 12: Server Configuration Loader
- Example 13: Validation and Error Handling
- Performance Tips
- Limitations
- Changelog
- Copy
json_parser.incinto yourpawno/include/directory. - Include it in your script:
#include <json_parser>No plugins, no external dependencies. Pure Pawn.
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>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);
}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 | 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 |
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
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
| 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 [] |
Initializes the parser state. Called automatically by most functions. Safe to call multiple times.
JSON_Init();Returns: 1 always.
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.
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.
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).
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_DEPTHlevels - Escape sequences in strings:
\",\\,\/,\b,\f,\n,\r,\t - Negative numbers and decimal floats
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.
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.
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.
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.
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.
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_FLOATExample:
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);
}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.
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.
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.
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.
These functions let you traverse the node tree manually.
new parent = JSON_GetParent(childNode);Returns: Parent node ID, or JSON_INVALID_NODE.
new first = JSON_GetFirstChild(objectNode);Returns: First child node ID, or JSON_INVALID_NODE.
new last = JSON_GetLastChild(objectNode);Returns: Last child node ID, or JSON_INVALID_NODE.
new next = JSON_GetNextSibling(currentNode);Returns: Next sibling node ID, or JSON_INVALID_NODE.
new prev = JSON_GetPrevSibling(currentNode);Returns: Previous sibling node ID, or JSON_INVALID_NODE.
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.
Checks if an object contains a specific key.
if (JSON_ObjectHasKey(root, "email"))
{
// key exists
}Returns: bool.
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.
Returns the number of elements in an array.
new size = JSON_GetArraySize(arrayNode);
printf("Array has %d elements", size);Returns: Element count, or 0.
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
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 nodeReturns: Node ID or JSON_INVALID_NODE.
These combine JSON_GetByPath with the appropriate value getter:
new name[32];
JSON_GetStringByPath(root, "user.name", name);Returns: 1 on success, 0 on failure.
new score;
JSON_GetIntByPath(root, "user.scores[0]", score);Returns: 1 on success, 0 on failure.
new Float:x;
JSON_GetFloatByPath(root, "position.x", x);Returns: 1 on success, 0 on failure.
new bool:isAdmin;
JSON_GetBoolByPath(root, "user.admin", isAdmin);Returns: 1 on success, 0 on failure.
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.
JSON_SetStringByPath(docId, root, "user.email", "test@example.com");JSON_SetIntByPath(docId, root, "user.level", 5);JSON_SetFloatByPath(docId, root, "player.health", 85.5);JSON_SetBoolByPath(docId, root, "player.alive", true);These functions create new standalone nodes belonging to a document.
new obj = JSON_CreateObject(docId);new arr = JSON_CreateArray(docId);new strNode = JSON_CreateString(docId, "hello world");new intNode = JSON_CreateInt(docId, 42);new floatNode = JSON_CreateFloat(docId, 3.14);new boolNode = JSON_CreateBool(docId, true);new nullNode = JSON_CreateNull(docId);All return: Node ID on success, JSON_INVALID_NODE on failure.
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.
Shorthand to set a string value on an object.
JSON_ObjectSetString(obj, "name", "Ali");JSON_ObjectSetInt(obj, "age", 25);JSON_ObjectSetFloat(obj, "score", 99.5);JSON_ObjectSetBool(obj, "vip", true);JSON_ObjectSetNull(obj, "deletedAt");Removes a key and its value from an object.
JSON_ObjectRemoveKey(obj, "tempData");Returns: 1 if removed, 0 if key not found.
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(arr, docId, "hello");JSON_ArrayAppendInt(arr, docId, 42);JSON_ArrayAppendFloat(arr, docId, 3.14);JSON_ArrayAppendBool(arr, docId, true);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.
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.
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.
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.
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.
Shorthand for compact serialization.
new output[512];
JSON_Minify(root, output);Shorthand for pretty-printed serialization.
new output[1024];
JSON_Prettify(root, output);Serializes a node and writes it to a file.
JSON_SerializeToFile(root, "scriptfiles/output.json", true);Returns: 1 on success, 0 on failure.
Serializes the root node of a document.
new output[1024];
JSON_SerializeDoc(docId, output, sizeof(output), true);Saves a document to a file.
JSON_SaveDocument(docId, "scriptfiles/data.json");Returns: 1 on success, 0 on failure.
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 copyReturns: New node ID, or JSON_INVALID_NODE.
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.
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.
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.
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.
Recursively searches for an integer node with the given value.
new found = JSON_FindByIntValue(root, 42);Returns: Node ID or JSON_INVALID_NODE.
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);
}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);
}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.
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 pointsReturns: Node ID of the final node in the path, or JSON_INVALID_NODE.
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.
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.
Recursively counts all nodes in a subtree (including the node itself).
new total = JSON_CountNodes(root);
printf("Total nodes: %d", total);Returns the maximum nesting depth of a subtree.
new depth = JSON_GetMaxDepth(root);
printf("Max depth: %d", depth);Returns how many node slots are still available.
new free = JSON_GetFreeNodeCount();
printf("Free nodes: %d / %d", free, JSON_MAX_NODES);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
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.
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 = 1Returns: 1 on success, 0 on failure.
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.0Returns: 1 on success, 0 on failure.
Frees all documents and resets all internal state. Use with caution - destroys everything.
JSON_Cleanup();Returns: 1 always.
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"
| 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
}| 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 |
#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;
}#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]
}#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);
}
}#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;
}#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;
}#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;
}#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;
}#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;
}#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;
}#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;
}#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;
}#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();
}#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);
}
}-
Destroy documents as soon as you're done with them. Nodes are a shared global resource.
-
Use path queries (
JSON_GetIntByPath, etc.) for quick one-off reads. For iterating many children, useJSON_foreach_object/JSON_foreach_arrayinstead of repeated path lookups. -
Increase
JSON_MAX_NODESif you work with large JSON files. Monitor usage withJSON_GetFreeNodeCount(). -
Avoid parsing the same file repeatedly. Parse once, store the values you need, then destroy the document.
-
Use
JSON_Minifyfor network transmission or compact storage. UseJSON_Prettifyonly for human-readable output or debugging. -
For large arrays,
JSON_GetArrayElementis$O(n)$ since it traverses the linked list. If you need sequential access, iterate withJSON_foreach_arrayinstead. -
JSON_MergeObjectswith deeply nested structures creates clones of source nodes. Be mindful of node consumption.
| 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: |
| Thread safety | Not applicable (Pawn is single-threaded) |
| Array access |
|
- Basic JSON tokenizer and lexer
- Support for parsing flat JSON objects (no nesting)
JSON_Parse()andJSON_GetString()initial implementation- Internal node pool with fixed
JSON_MAX_NODES - Basic error reporting on malformed input
- 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
- Array index access in paths (
"items[0].name") JSON_GetArrayElement()andJSON_GetArraySize()- Tree navigation:
JSON_GetParent(),JSON_GetFirstChild(),JSON_GetNextSibling() JSON_GetLastChild()andJSON_GetPrevSibling()JSON_GetChildCount()added
JSON_CreateDocument()andJSON_DestroyDocument()- Node creation functions:
JSON_CreateObject(),JSON_CreateArray(),JSON_CreateString(),JSON_CreateInt(),JSON_CreateFloat(),JSON_CreateBool(),JSON_CreateNull() JSON_ObjectSetValue()for attaching nodes to objectsJSON_ArrayAppend()for pushing nodes into arraysJSON_GetRootNode()added
JSON_Serialize()with compact output- Pretty-print support via
prettyparameter (indentation with spaces) JSON_Minify()andJSON_Prettify()shorthand functions- Proper escaping of special characters in serialized output (
\",\\,\n,\t,\r)
JSON_ParseFile()- load and parse JSON from fileJSON_SerializeToFile()- write serialized JSON to fileJSON_SaveDocument()andJSON_SerializeDoc()helpers- Fixed buffer overflow on large file reads
JSON_ObjectSetString(),JSON_ObjectSetInt(),JSON_ObjectSetFloat(),JSON_ObjectSetBool(),JSON_ObjectSetNull()convenience functionsJSON_ObjectRemoveKey()with proper sibling re-linkingJSON_ObjectHasKey()boolean checkJSON_GetObjectChild()direct key lookup
JSON_ArrayAppendString(),JSON_ArrayAppendInt(),JSON_ArrayAppendFloat(),JSON_ArrayAppendBool()JSON_ArrayInsert()- insert at specific indexJSON_ArrayRemove()- remove by index with re-linking- Fixed off-by-one on array index resolution in paths
JSON_SetByPath()- set arbitrary node by dot pathJSON_SetStringByPath(),JSON_SetIntByPath(),JSON_SetFloatByPath(),JSON_SetBoolByPath()JSON_EnsurePath()- auto-create intermediate objects/arrays along a pathJSON_RemoveByPath()- delete node at path
JSON_Init()andJSON_Cleanup()for global state managementJSON_Validate()- lightweight syntax check without full parseJSON_GetLastError()andJSON_GetErrorMessage()with detailed error codesJSON_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()andJSON_BuildArray()quick-build helpers
JSON_CloneNode()- deep recursive copy of any subtreeJSON_MergeObjects()withoverwriteflag for conflict resolution- Fixed clone not preserving key names on nested objects
JSON_Equal()- deep recursive equality check (type + value + structure)JSON_FindByKey()- recursive search by key nameJSON_FindByStringValue()andJSON_FindByIntValue()- Fixed equality check failing on arrays with identical content but different node IDs
JSON_IterateObject()andJSON_IterateArray()manual iteration functionsJSON_foreach_objectmacro for clean object property loopsJSON_foreach_arraymacro for clean array element loopsJSON_GetObjectKeys()- extract all keys into a string array
JSON_NodeToString()- convert any node value to string representationJSON_NodeToInt()- coerce string/float/bool to integerJSON_NodeToFloat()- coerce string/int/bool to floatJSON_CountNodes()- count all nodes in a subtreeJSON_GetMaxDepth()- measure deepest nesting levelJSON_GetFreeNodeCount()- monitor available node pool slots
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
- 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_DEPTHnow enforced strictly during both parse and build- Escape sequence handling expanded (proper
\b,\fsupport) - 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/Boolnow requiredocIdparameter
- 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
- Fixed
JSON_ParseFile()failing on files with BOM (byte order mark) - Fixed
JSON_SetIntByPath()not creating parent array when path contains index
- Fixed
JSON_ArrayInsert()corrupting sibling chain when inserting at index 0 - Fixed
JSON_GetObjectKeys()returning stale keys afterJSON_ObjectRemoveKey()
- 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
- 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
This library is provided as-is for use in SA-MP Pawn scripts. Feel free to use, modify, and distribute.