/** * vim: set ts=4 : * ============================================================================= * sm-json * Provides a pure SourcePawn implementation of JSON encoding and decoding. * https://github.com/clugg/sm-json * * sm-json (C)2018 James Dickens. (clug) * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. * ============================================================================= * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 3.0, as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . * * As a special exception, AlliedModders LLC gives you permission to link the * code of this program (as well as its derivative works) to "Half-Life 2," the * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software * by the Valve Corporation. You must obey the GNU General Public License in * all respects for all other code used. Additionally, AlliedModders LLC grants * this exception to all derivative works. AlliedModders LLC defines further * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), * or . */ #if defined _json_object_included #endinput #endif #define _json_object_included #include #include #include #include methodmap JSON_Object < StringMap { /** * Creates a new JSON_Object. * * @param is_array Should the object created be an array? [optional, default: false] * @returns A new JSON_Object. */ public JSON_Object(bool is_array = false) { StringMap obj = CreateTrie(); if (is_array) { obj.SetValue(JSON_ARRAY_INDEX_KEY, 0); } return view_as(obj); } /** * Checks whether the object has a key. * * @param key Key to check existence of. * @returns True if the key exists, false otherwise. */ public bool HasKey(const char[] key) { int dummy_int; char dummy_str[1]; return this.GetValue(key, dummy_int) || this.GetString(key, dummy_str, sizeof(dummy_str)); } /** * @section Array helpers. */ /** * Whether the current object is an array. */ property bool IsArray { public get() { return this.HasKey(JSON_ARRAY_INDEX_KEY); } } /** * The current index of the object if it is an array, or -1 otherwise. */ property int CurrentIndex { public get() { if (!this.IsArray) { return -1; } int result; return (this.GetValue(JSON_ARRAY_INDEX_KEY, result)) ? result : -1; } } /** * The number of items in the object if it is an array, * or the number of keys (including meta-keys) otherwise. */ property int Length { public get() { StringMapSnapshot snap = this.Snapshot(); int length = (this.IsArray) ? this.CurrentIndex : snap.Length; delete snap; return length; } } /** * Increments the current index of the object. * * @returns True on success, false if the current object is not an array. */ public bool IncrementIndex() { int current = this.CurrentIndex; if (current == -1) { return false; } return this.SetValue(JSON_ARRAY_INDEX_KEY, current + 1); } /** * Gets the string representation of an array index. * * @param output String buffer to store output. * @param maxlen Maximum size of string buffer. * @param key Key to get string for. [optional, default: current index] * @returns True on success, false otherwise. */ public int GetIndexString(char[] output, int maxlen, int key = -1) { key = (key == -1) ? this.CurrentIndex : key; if (key == -1) { return false; } return IntToString(key, output, maxlen); } /** * Removes an item from the object by index. * * @param index Index of object to remove. * @returns True on success, false if the current object * is not an array or the index does not exist. */ public bool RemoveIndexed(int index) { char key[JSON_INDEX_BUFFER_SIZE]; if (!this.GetIndexString(key, sizeof(key), index)) { return false; } return this.Remove(key); } /** * @section Internal getters. */ /** * Gets the cell type stored at a key. * * @param key Key to get value type for. * @returns Value type for key provided, or Type_Invalid if it does not exist. */ public JSON_CELL_TYPE GetKeyType(const char[] key) { int maxlen = strlen(key) + strlen(JSON_META_TYPE_KEY) + 1; char[] type_key = new char[maxlen]; Format(type_key, maxlen, "%s%s", key, JSON_META_TYPE_KEY); JSON_CELL_TYPE type; return (this.GetValue(type_key, type)) ? type : Type_Invalid; } /** * Gets the cell type stored at an index. * * @param index Index to get value type for. * @returns Value type for index provided, or Type_Invalid if it does not exist. */ public JSON_CELL_TYPE GetKeyTypeIndexed(int index) { char key[JSON_INDEX_BUFFER_SIZE]; if (!this.GetIndexString(key, sizeof(key), index)) { return Type_Invalid; } return this.GetKeyType(key); } /** * Gets the length of the string stored at a key. * * @param key Key to get string length for. * @returns Length of string at key provided, or -1 if it is not a string/does not exist. */ public int GetKeyLength(const char[] key) { int maxlen = strlen(key) + strlen(JSON_META_LENGTH_KEY) + 1; char[] length_key = new char[maxlen]; Format(length_key, maxlen, "%s%s", key, JSON_META_LENGTH_KEY); int length; return (this.GetValue(length_key, length)) ? length : -1; } /** * Gets the length of the string stored at an index. * * @param index Index to get string length for. * @returns Length of string at index provided, or -1 if it is not a string/does not exist. */ public int GetKeyLengthIndexed(int index) { char key[JSON_INDEX_BUFFER_SIZE]; if (!this.GetIndexString(key, sizeof(key), index)) { return -1; } return this.GetKeyLength(key); } /** * Gets whether the key should be hidden from encoding. * * @param key Key to get hidden state for. * @returns Whether or not the key should be hidden. */ public bool GetKeyHidden(const char[] key) { int maxlen = strlen(key) + strlen(JSON_META_HIDDEN_KEY) + 1; char[] length_key = new char[maxlen]; Format(length_key, maxlen, "%s%s", key, JSON_META_HIDDEN_KEY); bool hidden; return (this.GetValue(length_key, hidden)) ? hidden : false; } /** * Gets whether the index should be hidden from encoding. * * @param index Index to get hidden state for. * @returns Whether or not the index should be hidden. */ public bool GetKeyHiddenIndexed(int index) { char key[JSON_INDEX_BUFFER_SIZE]; if (!this.GetIndexString(key, sizeof(key), index)) { return false; } return this.GetKeyHidden(key); } /** * @section Internal setters. */ /** * Sets the cell type stored at a key. * * @param key Key to set value type for. * @param type Type to set key to. * @returns True on success, false otherwise. */ public bool SetKeyType(const char[] key, JSON_CELL_TYPE type) { int maxlen = strlen(key) + strlen(JSON_META_TYPE_KEY) + 1; char[] type_key = new char[maxlen]; Format(type_key, maxlen, "%s%s", key, JSON_META_TYPE_KEY); return this.SetValue(type_key, type); } /** * Sets the cell type stored at an index. * * @param index Index to set value type for. * @param type Type to set index to. * @returns True on success, false otherwise. */ public bool SetKeyTypeIndexed(int index, JSON_CELL_TYPE value) { char key[JSON_INDEX_BUFFER_SIZE]; if (!this.GetIndexString(key, sizeof(key), index)) { return false; } return this.SetKeyType(key, value); } /** * Sets the length of the string stored at a key. * * @param key Key to set string length for. * @param length Length to set string to. * @returns True on success, false otherwise. */ public bool SetKeyLength(const char[] key, int length) { int maxlen = strlen(key) + strlen(JSON_META_LENGTH_KEY) + 1; char[] length_key = new char[maxlen]; Format(length_key, maxlen, "%s%s", key, JSON_META_LENGTH_KEY); return this.SetValue(length_key, length); } /** * Sets the length of the string stored at an index. * * @param index Index to set string length for. * @param length Length to set string to. * @returns True on success, false otherwise. */ public bool SetKeyLengthIndexed(int index, int length) { char key[JSON_INDEX_BUFFER_SIZE]; if (!this.GetIndexString(key, sizeof(key), index)) { return false; } return this.SetKeyLength(key, length); } /** * Sets whether the key should be hidden from encoding. * * @param key Key to set hidden state for. * @param hidden Wheter or not the key should be hidden. * @returns True on success, false otherwise. */ public bool SetKeyHidden(const char[] key, bool hidden) { int maxlen = strlen(key) + strlen(JSON_META_HIDDEN_KEY) + 1; char[] hidden_key = new char[maxlen]; Format(hidden_key, maxlen, "%s%s", key, JSON_META_HIDDEN_KEY); return this.SetValue(hidden_key, hidden); } /** * Sets whether the index should be hidden from encoding. * * @param index Index to set hidden state for. * @param hidden Wheter or not the index should be hidden. * @returns True on success, false otherwise. */ public bool SetKeyHiddenIndexed(int index, bool hidden) { char key[JSON_INDEX_BUFFER_SIZE]; if (!this.GetIndexString(key, sizeof(key), index)) { return false; } return this.SetKeyHidden(key, hidden); } /** * @section Object getters. */ // GetString is implemented natively by StringMap /** * Retrieves the string stored at an index. * * @param index Index to retrieve string value for. * @param value String buffer to store output. * @param maxlen Maximum size of string buffer. * @returns True on success. False if the key is not set, or the key is set as a value or array (not a string). */ public bool GetStringIndexed(int index, char[] value, int max_size) { char key[JSON_INDEX_BUFFER_SIZE]; if (!this.GetIndexString(key, sizeof(key), index)) { return false; } return this.GetString(key, value, max_size); } /** * Retrieves the int stored at a key. * * @param key Key to retrieve int value for. * @returns Value stored at key. */ public int GetInt(const char[] key) { int value; return (this.GetValue(key, value)) ? value : -1; } /** * Retrieves the int stored at an index. * * @param index Index to retrieve int value for. * @returns Value stored at index. */ public int GetIntIndexed(int index) { char key[JSON_INDEX_BUFFER_SIZE]; if (!this.GetIndexString(key, sizeof(key), index)) { return -1; } return this.GetInt(key); } /** * Retrieves the float stored at a key. * * @param key Key to retrieve float value for. * @returns Value stored at key. */ public float GetFloat(const char[] key) { float value; return (this.GetValue(key, value)) ? value : -1.0; } /** * Retrieves the float stored at an index. * * @param index Index to retrieve float value for. * @returns Value stored at index. */ public float GetFloatIndexed(int index) { char key[JSON_INDEX_BUFFER_SIZE]; if (!this.GetIndexString(key, sizeof(key), index)) { return -1.0; } return this.GetFloat(key); } /** * Retrieves the bool stored at a key. * * @param key Key to retrieve bool value for. * @returns Value stored at key. */ public bool GetBool(const char[] key) { bool value; return (this.GetValue(key, value)) ? value : false; } /** * Retrieves the bool stored at an index. * * @param index Index to retrieve bool value for. * @returns Value stored at index. */ public bool GetBoolIndexed(int index) { char key[JSON_INDEX_BUFFER_SIZE]; if (!this.GetIndexString(key, sizeof(key), index)) { return false; } return this.GetBool(key); } /** * Retrieves the handle stored at a key. * * @param key Key to retrieve handle value for. * @returns Value stored at key. */ public Handle GetHandle(const char[] key) { Handle value; return (this.GetValue(key, value)) ? value : null; } /** * Retrieves the handle stored at an index. * * @param index Index to retrieve handle value for. * @returns Value stored at index. */ public Handle GetHandleIndexed(int index) { char key[JSON_INDEX_BUFFER_SIZE]; if (!this.GetIndexString(key, sizeof(key), index)) { return null; } return this.GetHandle(key); } /** * Retrieves the object stored at a key. * * @param key Key to retrieve object value for. * @returns Value stored at key. */ public JSON_Object GetObject(const char[] key) { return view_as(this.GetHandle(key)); } /** * Retrieves the object stored at an index. * * @param index Index to retrieve object value for. * @returns Value stored at index. */ public JSON_Object GetObjectIndexed(int index) { char key[JSON_INDEX_BUFFER_SIZE]; if (!this.GetIndexString(key, sizeof(key), index)) { return null; } return this.GetObject(key); } /** * @section Object setters. */ /** * Sets the string stored at a key. * * @param key Key to set to string value. * @param value Value to set. * @returns True on success, false otherwise. */ public bool SetString(const char[] key, const char[] value, bool replace = true) { return this.SetString(key, value, replace) && this.SetKeyType(key, Type_String) && this.SetKeyLength(key, strlen(value)); } /** * Sets the string stored at an index. * * @param index Index to set to string value. * @param value Value to set. * @returns True on success, false otherwise. */ public bool SetStringIndexed(int index, const char[] value, bool replace = true) { char key[JSON_INDEX_BUFFER_SIZE]; if (!this.GetIndexString(key, sizeof(key), index)) { return false; } return this.SetString(key, value, replace); } /** * Sets the int stored at a key. * * @param key Key to set to int value. * @param value Value to set. * @returns True on success, false otherwise. */ public bool SetInt(const char[] key, int value, bool replace = true) { return this.SetValue(key, value, replace) && this.SetKeyType(key, Type_Int); } /** * Sets the int stored at an index. * * @param index Index to set to int value. * @param value Value to set. * @returns True on success, false otherwise. */ public bool SetIntIndexed(int index, int value, bool replace = true) { char key[JSON_INDEX_BUFFER_SIZE]; if (!this.GetIndexString(key, sizeof(key), index)) { return false; } return this.SetInt(key, value, replace); } /** * Sets the float stored at a key. * * @param key Key to set to float value. * @param value Value to set. * @returns True on success, false otherwise. */ public bool SetFloat(const char[] key, float value, bool replace = true) { return this.SetValue(key, value, replace) && this.SetKeyType(key, Type_Float); } /** * Sets the float stored at an index. * * @param index Index to set to float value. * @param value Value to set. * @returns True on success, false otherwise. */ public bool SetFloatIndexed(int index, float value, bool replace = true) { char key[JSON_INDEX_BUFFER_SIZE]; if (!this.GetIndexString(key, sizeof(key), index)) { return false; } return this.SetFloat(key, value, replace); } /** * Sets the bool stored at a key. * * @param key Key to set to bool value. * @param value Value to set. * @returns True on success, false otherwise. */ public bool SetBool(const char[] key, bool value, bool replace = true) { return this.SetValue(key, value, replace) && this.SetKeyType(key, Type_Bool); } /** * Sets the bool stored at an index. * * @param index Index to set to bool value. * @param value Value to set. * @returns True on success, false otherwise. */ public bool SetBoolIndexed(int index, bool value, bool replace = true) { char key[JSON_INDEX_BUFFER_SIZE]; if (!this.GetIndexString(key, sizeof(key), index)) { return false; } return this.SetBool(key, value, replace); } /** * Sets the handle stored at a key. * * @param key Key to set to handle value. * @param value Value to set. * @returns True on success, false otherwise. */ public bool SetHandle(const char[] key, Handle value = null, bool replace = true) { return this.SetValue(key, value, replace) && this.SetKeyType(key, Type_Null); } /** * Sets the handle stored at an index. * * @param index Index to set to handle value. * @param value Value to set. * @returns True on success, false otherwise. */ public bool SetHandleIndexed(int index, Handle value = null, bool replace = true) { char key[JSON_INDEX_BUFFER_SIZE]; if (!this.GetIndexString(key, sizeof(key), index)) { return false; } return this.SetHandle(key, value, replace); } /** * Sets the object stored at a key. * * @param key Key to set to object value. * @param value Value to set. * @returns True on success, false otherwise. */ public bool SetObject(const char[] key, JSON_Object value, bool replace = true) { return this.SetValue(key, value, replace) && this.SetKeyType(key, Type_Object); } /** * Sets the object stored at an index. * * @param index Index to set to object value. * @param value Value to set. * @returns True on success, false otherwise. */ public bool SetObjectIndexed(int index, JSON_Object value, bool replace = true) { char key[JSON_INDEX_BUFFER_SIZE]; if (!this.GetIndexString(key, sizeof(key), index)) { return false; } return this.SetObject(key, value, replace); } /** * @section Array setters. */ /** * Pushes a string to the end of the array. * * @param value Value to push. * @returns True on success, false otherwise. */ public bool PushString(const char[] value) { return this.SetStringIndexed(this.CurrentIndex, value) && this.IncrementIndex(); } /** * Pushes an int to the end of the array. * * @param value Value to push. * @returns True on success, false otherwise. */ public bool PushInt(int value) { return this.SetIntIndexed(this.CurrentIndex, value) && this.IncrementIndex(); } /** * Pushes a float to the end of the array. * * @param value Value to push. * @returns True on success, false otherwise. */ public bool PushFloat(float value) { return this.SetFloatIndexed(this.CurrentIndex, value) && this.IncrementIndex(); } /** * Pushes a bool to the end of the array. * * @param value Value to push. * @returns True on success, false otherwise. */ public bool PushBool(bool value) { return this.SetBoolIndexed(this.CurrentIndex, value) && this.IncrementIndex(); } /** * Pushes a handle to the end of the array. * * @param value Value to push. * @returns True on success, false otherwise. */ public bool PushHandle(Handle value = null) { return this.SetHandleIndexed(this.CurrentIndex, value) && this.IncrementIndex(); } /** * Pushes an object to the end of the array. * * @param value Value to push. * @returns True on success, false otherwise. */ public bool PushObject(JSON_Object value) { return this.SetObjectIndexed(this.CurrentIndex, value) && this.IncrementIndex(); } /** * @section Generic. */ /** * Encodes the object into its string representation. * * @param output String buffer to store output. * @param maxlen Maximum size of string buffer. * @param pretty_print Should the output be pretty printed (newlines and spaces)? [optional, default: false] * @param depth The current depth of the encoder. [optional, default: 0] */ public void Encode(char[] output, int maxlen, bool pretty_print = false, int depth = 0) { json_encode(this, output, maxlen, pretty_print, depth); } /** * Decodes a JSON string into this object. * * @param buffer Buffer to decode. */ public void Decode(const char[] buffer) { json_decode(buffer, this); } /** * Recursively cleans up the object and any objects referenced within. */ public void Cleanup() { json_cleanup(this); } };