Skip to content

Conversation

@ProjectSky
Copy link

Features

  • Relies on YYJSON which is High-performance JSON library written in C
  • High-performance JSON parsing and serialization (Simple Performance Test Report )
  • Support for JSON Pointer operations
  • x64 support
  • Easy-to-use API for both objects and arrays
  • Pretty printing and writing support
  • Array and object sorting support
  • Iteration methods for arrays and objects
  • Support for both mutable and immutable JSON documents
  • Support SourceMod Extension API

Basic Examples

Working with Objects

// Create a JSON object
YYJSONObject obj = new YYJSONObject();
obj.SetInt("int", 1);
obj.SetInt64("int64", "9223372036854775800");
obj.SetFloat("float", 2.0);
obj.SetBool("bool", true);
obj.SetString("str", "Hello World");
obj.SetNull("null");

/* Output:
{
  "int": 1,
  "int64": 9223372036854775800,
  "float": 2.0,
  "bool": true,
  "str": "Hello World",
  "null": null
}
*/

delete obj;

Working with Arrays

// Create a JSON array
YYJSONArray arr = new YYJSONArray();
arr.PushInt(1);
arr.PushInt64("9223372036854775800");
arr.PushFloat(2.0);
arr.PushBool(true);
arr.PushString("Hello World");
arr.PushNull();

/* Output:
[
  1,
  9223372036854775800,
  2.0,
  true,
  "Hello World",
  null
]
*/

delete arr;

Advanced Features

Using JSON Pointer

// Create nested structures
YYJSONObject obj = new YYJSONObject();
obj.PtrSetInt("/a/b/c", 1);

/* Output:
{
  "a": {
    "b": {
      "c": 1
    }
  }
}
*/

delete obj;

/* example.json:
{
  "int": 1234,
  "arr": [1, 1.2344, 3],
  "nested": {
    "obj": {
      "value": 42
    }
  }
}
*/

YYJSONObject data = YYJSON.Parse("example.json", true);
// Access values using JSON Pointer
int value = data.PtrGetInt("/int"); // Returns: 1234
float fValue = data.PtrGetFloat("/arr/1"); // Returns: 1.2344
int nested = data.PtrGetInt("/nested/obj/value"); // Returns: 42
delete data;

Array and Object Iteration

// Object iteration
YYJSONObject obj = YYJSON.Parse("{\"a\": 1, \"b\": 2, \"c\": 3}");
char key[64];
YYJSON value;

// Method 1: Using Foreach (Recommended)
while (obj.ForeachObject(key, sizeof(key), value)) {
  PrintToServer("Key: %s", key);
  delete value;
}

// Method 2: Using ForeachKey (only used for keys)
while (obj.ForeachKey(key, sizeof(key))) {
  PrintToServer("Key: %s", key);
}

// Method 3: Classic iteration
for (int i = 0; i < obj.Size; i++) {
  obj.GetKey(i, key, sizeof(key));
  value = obj.GetValueAt(i);
  delete value;
}

delete obj;

// Array iteration
YYJSONArray arr = YYJSON.Parse("[1, 2, 3, 4, 5]");
int index;

// Method 1: Using Foreach (Recommended)
while (arr.ForeachArray(index, value)) {
  PrintToServer("Index: %d", index);
  delete value;
}

// Method 2: Using ForeachIndex (only used for index)
while (arr.ForeachIndex(index)) {
  PrintToServer("Index: %d", index);
}

// Method 3: Classic iteration
for (int i = 0; i < arr.Length; i++) {
  value = arr.Get(i);
  delete value;
}

delete arr;

Array Search Operations

// Create a test array
YYJSONArray arr = YYJSON.Parse(
  "[42, true, \"hello\", 3.14, \"world\", false, 42]"
);

// Search for values (returns first occurrence)
int index;
index = arr.IndexOfInt(42);           // Returns 0
index = arr.IndexOfBool(true);        // Returns 1
index = arr.IndexOfString("hello");   // Returns 2
index = arr.IndexOfFloat(3.14);       // Returns 3
index = arr.IndexOfString("world");   // Returns 4
index = arr.IndexOfBool(false);       // Returns 5

// Search for non-existent values
index = arr.IndexOfInt(999);          // Returns -1
index = arr.IndexOfString("missing"); // Returns -1
index = arr.IndexOfFloat(2.718);      // Returns -1

delete arr;

Sorting Arrays and Objects

// Array sorting
YYJSONArray arr = YYJSON.Parse(
  "[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]", .is_mutable_doc = true
);

arr.Sort(); // Ascending (default)
// [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]

arr.Sort(YYJSON_SORT_DESC); // Descending
// [9, 6, 5, 5, 5, 4, 3, 3, 2, 1, 1]

arr.Sort(YYJSON_SORT_RANDOM); // Random
// [5, 2, 9, 1, 6, 3, 4, 5, 1, 3, 5] (example output)

// Mixed type array sorting
YYJSONArray mixed = YYJSON.Parse(
  "[true, 42, \"hello\", 1.23, false, \"world\"]", .is_mutable_doc = true
);
mixed.Sort();
// [false, true, 1.23, 42, "hello", "world"]

// Object sorting by keys
YYJSONObject obj = YYJSON.Parse(
  "{\"zebra\": 1, \"alpha\": 2, \"beta\": 3, \"gamma\": 4}", .is_mutable_doc = true
);

obj.Sort(); // Ascending (default)
// {"alpha": 2, "beta": 3, "gamma": 4, "zebra": 1}

obj.Sort(YYJSON_SORT_DESC); // Descending
// {"zebra": 1, "gamma": 4, "beta": 3, "alpha": 2}

obj.Sort(YYJSON_SORT_RANDOM); // Random
// {"beta": 3, "zebra": 1, "alpha": 2, "gamma": 4} (example output)

delete arr;
delete mixed;
delete obj;

Using FromStrings

// Create object from key-value string arrays
char pairs[][] = {"name", "test", "type", "demo", "version", "1.0.0"};
  
YYJSONObject obj = YYJSONObject.FromStrings(pairs, sizeof(pairs));

/* Output:
{
  "name": "test",
  "type": "demo", 
  "version": "1.0.0"
}
*/

// Create array from string array
char items[][] = {"apple", "banana", "orange"};
YYJSONArray arr = YYJSONArray.FromStrings(items, sizeof(items));

/* Output:
[
  "apple",
  "banana", 
  "orange"
]
*/

delete obj;
delete arr;

Using JSON Pack

// Create object with mixed types
YYJSON packed = YYJSON.Pack("{s:s,s:i,s:f,s:b,s:n}",
  "name", "John",
  "age", 25,
  "height", 1.75,
  "active", true,
  "extra"
);

/* Output:
{
  "name": "John",
  "age": 25,
  "height": 1.75,
  "active": true,
  "extra": null
}
*/

// Create nested structures
YYJSON nested = YYJSON.Pack("{s:{s:s,s:[i,i,i]}}",
  "user",
    "name", "John",
    "scores", 85, 90, 95
);

/* Output:
{
  "user": {
    "name": "John",
    "scores": [85, 90, 95]
  }
}
*/

// Create array with mixed types
YYJSON array = YYJSON.Pack("[s,i,f,b,n]",
    "test", 42, 3.14, true
);

/* Output:
[
  "test",
  42,
  3.14,
  true,
  null
]
*/

delete packed;
delete nested;
delete array;

Working with Immutable Documents

When parsing JSON documents, you can choose whether to create a mutable or immutable document:

// Create an immutable document (read-only)
YYJSONObject obj = YYJSON.Parse("example.json", true);

// Create a mutable document (read-write)
YYJSONObject obj = YYJSON.Parse("example.json", true, true);

Immutable documents:

  • Are read-only and cannot be modified
  • Use less memory
  • Throw errors when attempting modification operations

Operations on Immutable Documents

Immutable documents support a variety of read operations:

  • Type Checking: You can check the type of values within the document.
  • Value Retrieval: You can retrieve values using keys or indices.
  • Iteration: You can iterate over arrays and objects.
  • Comparison: You can compare immutable documents with other documents.

Example of operations with immutable documents:

// Create an immutable document
YYJSONObject obj = YYJSON.Parse("example.json", true);

// Reading is allowed
int value = obj.GetInt("key"); // Works fine
float fValue = obj.GetFloat("key2"); // Works fine

// Modifications will fail with clear error messages
obj.SetInt("key", 123); // Error: Cannot set value in an immutable JSON object
obj.Remove("key"); // Error: Cannot remove value from an immutable JSON object
obj.Sort(); // Error: Cannot sort an immutable JSON object

delete obj;

Converting Between Mutable and Immutable

You can convert between mutable and immutable documents using deep copy:

// Create an immutable document
YYJSONObject immutable = YYJSON.Parse("example.json", true);

// Create a mutable copy
YYJSONObject mutable = immutable.ToMutable();

// Now you can modify the mutable copy
mutable.SetInt("key", 123);

delete mutable;
delete immutable;

@Headline
Copy link
Member

Headline commented Oct 18, 2025

Hello - Have you spoken with anyone prior to proposing this?

Sounds like it's a good extension, but not one that's ubiquitous enough to be included with sourcemod

@ProjectSky
Copy link
Author

Hello - Have you spoken with anyone prior to proposing this?

Sounds like it's a good extension, but not one that's ubiquitous enough to be included with sourcemod

Not yet. I'm suggesting that SourceMod should have a modern, official JSON extension, considering AMXX has long included this capability alliedmodders/amxmodx#379

@A1mDev
Copy link
Contributor

A1mDev commented Oct 20, 2025

The extension looks useful and well-designed.
It seems particularly valuable for exchanging data between external services. Perhaps an extension like REST in Pawn could complement it, although I believe that has already been suggested.

@Kenzzer
Copy link
Member

Kenzzer commented Oct 24, 2025

Throwing in my two cents. This looks nice, but much like headline said I think this should have been talked over before opening a PR. Historically when sourcemod takes in new extensions, the accepted extension is already well established into the plugin ecosystem. This has two benefits :

  • We know it's robust, because it has been field tested for years
  • We don't cause any plugins rewrite

REST in Pawn has been mentioned, and it is indeed (at least to me) the go-to JSON extension for plugins. Accepting this extension instead would mean that the plugin authors who care about JSON capabilities will have to migrate to this API (and maybe uncover bugs in the process). I don't think it's a dealbreaker, but I'm mentioning why accepting could cause troubles.

Last, and this is mostly a nitpick. I don't think it should ever be leaked to plugins what kind of json's flavor they're using.
YYJson > Json or SMJson or something
If YYJson is ever revealed as problematic, or we want rapidjson or nlohmann instead, we don't want to cause confusion. So let's keep the name anonymous.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants