A lightweight and lightning-fast C++ JSON & JSON5 parser designed for high performance and minimal footprint.
- Zero-dependency: Easy to integrate into any C++ project.
- Minimal memory overhead: Ideal for resource-constrained environments.
- High-performance parsing: Benchmarked for speed against other leading parsers.
- Simple API: Intuitive and easy to use.
- Supported JSON
- Data types
- Simple examples
- Getting Started
- Building and testing the project
- Data Range
- TODO
- Supported CPP
See more in the specifications document.
TinyJSON is a fully compliant JSON5 parser. JSON5 is a superset of JSON that makes it easier for humans to write and maintain.
- Objects: Keys can be unquoted identifiers (e.g.,
{name: "value"}) or single-quoted. Trailing commas are allowed. - Arrays: Trailing commas are allowed.
- Strings: Can be single-quoted (
'hello'). Can span multiple lines by escaping newlines. Support for additional escapes like\v,\0,\xHH, and\u{HHHHH}. - Numbers: Can be hexadecimal (
0x123). Can have leading or trailing decimal points (.5,5.). Support forNaNandInfinity. Can start with an explicit plus sign (+42). - Comments: Support for single-line (
//) and multi-line (/* ... */) comments. - Whitespace: Support for additional Unicode whitespace characters.
To parse JSON5, you must set the specification flag in parse_options.
#include "TinyJSON.h"
#include <iostream>
using namespace TinyJSON;
int main()
{
const char* json5_text = R"(
{
// A comment
unquoted: 'single quoted string',
hex: 0xFF,
array: [1, 2, 3,], // trailing comma
nested: {
val: .5 // leading decimal
}
}
)";
parse_options options;
options.specification = parse_options::json5_1_0_0;
options.throw_exception = true;
try {
auto* tj = TJ::parse(json5_text, options);
if (tj) {
auto* obj = dynamic_cast<TJValueObject*>(tj);
std::cout << "Hex value: " << obj->get_number<int>("hex") << std::endl;
delete tj;
}
} catch (const TJParseException& e) {
std::cerr << "Parse error: " << e.what() << std::endl;
}
return 0;
}| JSON | c++/TinyJSON |
|---|---|
| object | TJValueObject |
| array | TJValueArray |
| string | TJValueString |
| number | TJValueNumber (long long, long double, exponent) |
| true | TJValueBoolean (true) |
| false | TJValueBoolean (false) |
| null | TJValueNull (nullptr) |
We follow Semantic Versioning 2.0.0, semver.org
- MAJOR version when you make incompatible API changes
- MINOR version when you add functionality in a backward compatible manner
- PATCH version when you make backward compatible bug fixes
The version is set in the TinyJSON.h file.
static const short TJ_VERSION_MAJOR = 0;
static const short TJ_VERSION_MINOR = 2;
static const short TJ_VERSION_PATCH = 0;
static const char TJ_VERSION_STRING[] = "0.2.0";- Depth: (
max_depth:64) You can set how deep you want to allow the parsing to go. - Throw: (
throw_exception:false) If you want to throw exceptions or simply return null. - Strict: (
strict:false) If true, getter methods (likeget_boolean,get_string, etc.) will throw an exception if the key is not found or the type is incorrect. If false, they will return a default value (e.g., 0, "", false). - Specification: (
specification:parse_options::rfc8259) What specs will the parser be following/enforcing.- rfc4627
- rfc7159
- rfc8259
- json5_1_0_0
- Callback: (
callback_function:std::function<void(message_type, const TJCHAR*)>) Callback function called where there is an error, warning etc. NB: The Callback function is called even if Throw is false.- trace
- debug
- info
- warning
- error
- fatal
For example ...
TinyJSON::parse_options options = {};
options.throw_exception = true;
options.max_depth = 10;
options.Callback = [&](TinyJSON::parse_options::message_type message_type, const TJCHAR* exception_message) {
// something, something ...
};
try
{
auto blah = TinyJSON::TJ::parse("[0123]", options)
...
}
catch(TinyJSON::TJParseException ex)
{
ex.what(); // Numbers cannot have leading zeros
}- Throw: (
throw_exception:false) If you want to throw exceptions or simply return false. - write_formatting: (
write_formatting::indented) The formatting we want to write the file with.- none
- indented
- Byte order mark: (
byte_order_mark:none)- none
- uft8
For example ...
TinyJSON::write_options options = {};
options.throw_exception = true;
options.write_formatting = TinyJSON::formatting::minify;
try
{
...
// get JSON somewhere or create it.
...
if( TinyJSON::TJ::write_file("file.json", *json, options))
{
// all good
}
...
}
catch(TinyJSON::TJParseException ex)
{
ex.what(); // what?
}The parsing exception is TinyJSON::TJParseException and can be made optional in the TinyJSON::parse_options flag.
TinyJSON::parse_options options = {};
options.throw_exception = true;
try
{
auto blah = TinyJSON::TJ::parse("[0123]", options)
...
}
catch(TinyJSON::TJParseException ex)
{
ex.what(); // Numbers cannot have leading zeros
}The parsing exception is TinyJSON::TJParseException and can be made optional in the TinyJSON::parse_options flag.
TinyJSON::write_options options = {};
options.throw_exception = true;
options.write_formatting = TinyJSON::formatting::minify;
try
{
auto json = TinyJSON::TJ::parse( "{ \"Hello\" : \"World\" }" );
TinyJSON::TJ::write_file("file.json", *json, options)
...
}
catch(TinyJSON::TJWriteException ex)
{
ex.what(); // some write error
}You can simply call the TinyJSON::TJ::is_valid(...) method
if(TinyJSON::TJ::is_valid("[123,456]"))
{
// do this
}
else
{
// do that
}To read a JSON file you simply need to call the static method parse_file, the extention does not have to be .json
auto json = TinyJSON::TJ::parse_file( "myfile.json" );
...
delete json;This will then return an object that you can inspect as per normal.
To read a JSON string you simply need to call the static method parse
auto json = TinyJSON::TJ::parse( "{ \"Hello\" : \"World\" }" );
...
delete json;This will then return an object that you can inspect.
...
auto value = json->try_get_string("Hello"); // "World"
auto no_value = json->try_get_string("Life"); // null
...You can also use literals
using namespace TinyJSON;
...
auto json = "[12,13,14]"_tj;
// use it.
...
delete json;
...Or just use the string directly
You can convert the json to 'pretty' indented json
#define TJ_INCLUDE_STD_STRING 1
#include "TinyJSON.h"
using namespace TinyJSON;
...
// output a pretty JSON
std::cout << "[12,13,14]"_tj_indent;
...Or minify code.
#define TJ_INCLUDE_STD_STRING 1
#include "TinyJSON.h"
using namespace TinyJSON;
...
// output a pretty JSON
std::cout << "[12 , 13 , 14]"_tj_minify;
...There are 2 ways to find a key, case sensitive or insensitive, (the default is case sensitive).
You can load the json either by parsing or creating it manually.
auto object = new TinyJSON::TJValueObject();
object->set_string("a", "Hello");
object->set_string("b", "World");
object->set_string("c", "Bye");
...
// search for something
auto case_sensitive = object->try_get_value("Hello");
auto case_insensitive = object->try_get_value("HelLO", false); // all good as we don't care about case.
auto case_insensitive_not_found = object->try_get_value("HelLO"); // = null as the search was case sensitive
...
delete object;You can use has_key to check if an object contains a specific key.
auto json = TinyJSON::TJ::parse(R"({"a": 42})");
auto jobject = dynamic_cast<TinyJSON::TJValueObject*>(json);
if (jobject->has_key("a"))
{
// do something
}
// Case insensitive check
bool exists = jobject->has_key("A", false); // returns true auto object = new TinyJSON::TJValueObject();
object->set_string("a", "Hello");
object->set_string("b", "World");
object->set_string("c", "Bye");
object->set_boolean("d", true);
object->set_float("e", 42.15);
auto dump = object->dump();
/*
{
"a": "Hello",
"b": "World",
"c": "Bye",
"d": true,
"e": 42.15
}
*/You can also remove a value
auto object = new TinyJSON::TJValueObject();
object->set_string("a", "Hello");
object->set_string("b", "World");
object->set_string("c", "Bye");
object->set_boolean("d", true);
object->pop("b");
auto dump = object->dump();
/*
{
"a": "Hello",
"b": "World",
"c": "Bye",
"d": true
}
*/To write a JSON string you simply need to call the method write_file on the JSON object returned.
auto json = TinyJSON::TJ::parse( "{ \"Hello\" : \"World\" }" );
if( TinyJSON::TJ::write_file("file.json", *json))
{
// all good
}
else
{
// something broke
}
delete json;Regardless if you are parsing a file or parsing a string, TinyJSON will parse the file for UTF-8 DOM if there is one.
While you can dump a string to (re)write a json string you might want to use it directly in your code.
auto json = TinyJSON::TJ::parse( R"("This is a test \" with a quote")" );
auto actual_string = json->dump_string();
/*
This is a test " with a quote
*/
delete json;The formatting types are
TinyJSON::formatting::indented
TinyJSON::formatting::minifyEach objects are read into TJValue* classes of type TJValueObject.
auto json = TinyJSON::TJ::parse( "{ \"Hello\" : \"World\" }" );
auto value = json->try_get_string("Hello"); // "World"
auto no_value = json->try_get_string("Life"); // null
delete json;or
auto json = TinyJSON::TJ::parse( "{ \"Hello\" : \"World\" }" );
auto value = json->get_string("Hello"); // "World"
auto no_value = json->get_string("Life"); // ""
delete json;You can dynamically create a new object and then add a named values to it.
auto object = new TinyJSON::TJValueObject();
object->set_number("a", 42);
object->set_float("b", 0.005);
object->set_null("c");
...
delete json;Each arrays are read into TJValue* classes of type TJValueArray.
Then each items in the array are also TJValue* of type string, number and so on.
auto json = TinyJSON::TJ::parse( "[ 12, 14, 16 ]" );
auto array_of_values = dynamic_cast<TinyJSON::TJValueArray*>(json);
auto number_of_items = array_of_values->get_number_of_items(); // 3
auto value1 = array_of_values->at(0); //14
auto value2 = array_of_values->at(2); //16
auto value3 = array_of_values->at(5); // null
delete json;You can dynamically create a new array and then add string, numbers and so on.
auto json = new TinyJSON::TJValueArray();
json->add_boolean(true);
json->add_boolean(false);
json->add_number(42);
...
delete json;You can remove an item at a certain position using remove_at(index).
auto json = new TinyJSON::TJValueArray();
json->add_string("Hello");
json->add_string("Tiny");
json->add_string("World");
// Remove "Tiny" at index 1
json->remove_at(1);
...
delete json;Each TJValue* item can be of different type
Assuming the array below with multiple items, you can query the type of each TJValue* and do something accordingly.
auto json = TinyJSON::TJ::parse( "[ ... ]" );
auto array_of_values = dynamic_cast<TinyJSON::TJValueArray*>(json);
auto value = array_of_values->at(0);
if(value->is_object()) { /**/}
if(value->is_array()) { /**/}
if(value->is_string()) { /**/}
if(value->is_number()) { /**/}
if(value->is_true()) { /**/}
if(value->is_false()) { /**/}
if(value->is_null()) { /**/}
delete json; auto json = TinyJSON::TJ::parse(R"(
1,
2,
3
])");
for (auto& a : *json)
{
// do stuff
}
delete json;
...NB: You can technically everything...
auto json = TinyJSON::TJ::parse("2.5");
for (auto& a : *json)
{
// do stuff
}
...
delete json;The generic get<...>( ... ) method provides a convenient way to retrieve values of various types directly.
For TJValueObject (key-based):
auto json = TinyJSON::TJ::parse(R"({"a": 42, "b": "hello", "c": true})");
auto obj = dynamic_cast<TJValueObject*>(json);
int a = obj->get<int>("a"); // 42
std::string b = obj->get<std::string>("b"); // "hello"
bool c = obj->get<bool>("c"); // trueThe get_or<...>(key, default_value) method allows you to specify a default value if the key is not found or the type is incorrect.
auto json = TinyJSON::TJ::parse(R"({"a": 42})");
auto obj = dynamic_cast<TJValueObject*>(json);
int a = obj->get_or<int>("a", 0); // 42
int b = obj->get_or<int>("b", 100); // 100 (key not found)
std::string c = obj->get_or<std::string>("c", "default"); // "default"For any TJValue* (direct value):
auto val = obj->try_get_value("a");
int a = val->get<int>(); // 42The generic get also supports std::vector for arrays of numbers or floats:
auto json = TinyJSON::TJ::parse(R"({"ints": [1, 2, 3], "floats": [1.1, 2.2]})");
auto obj = dynamic_cast<TJValueObject*>(json);
```cpp
auto ints = obj->get<std::vector<int>>("ints");
auto doubles = obj->get<std::vector<double>>("floats");Getter methods on TJValue (or through TJValueObject keys) can be used to retrieve specific types. By default, these methods are non-strict and return default values if the type is incorrect. By setting strict: true in parse_options, these methods will throw a TJParseException instead.
- get_number()
- get_float()
- get_string()
- get_boolean()
- get_numbers()
- get_floats()
TinyJSON::parse_options options;
options.strict = true;
options.throw_exception = true;
auto json = TinyJSON::TJ::parse(R"(
{
"a": true,
"b": 123
}
)", options
);
auto jobject = dynamic_cast<TinyJSON::TJValueObject*>(json);
auto valuea = jobject->get_boolean("a"); // returns true
try {
auto valueb = jobject->get_string("b"); // THROW! "b" is a number, not a string
} catch (const TinyJSON::TJParseException& e) {
// Handle error
}The generic set<...>( ... ) method provides a convenient way to set values of various types directly.
For TJValueObject (key-based):
TinyJSON::TJValueObject obj;
obj.set("a", 42); // set int
obj.set("b", "hello"); // set string
obj.set("c", true); // set boolean
obj.set("d", 3.14); // set doubleThe generic set also supports std::string and std::vector for arrays of numbers or floats:
TinyJSON::TJValueObject obj;
std::string val = "hello";
obj.set("b", val); // set std::string
std::vector<int> ints = {1, 2, 3};
obj.set("ints", ints); // set std::vector<int>You can also use specific set methods:
- set_number(key, value)
- set_float(key, value)
- set_string(key, value)
- set_boolean(key, value)
- set_null(key)
TinyJSON::TJValueObject obj;
obj.set_number("a", 42);
obj.set_string("b", "hello");
obj.set_boolean("c", true);
obj.set_null("d");TinyJSON is designed to be as simple to integrate as possible.
You can simply copy src/TinyJSON.h and src/TinyJSON.cpp directly into your project.
Clone the repository into your project (e.g., in a third_party/ directory) and add it to your CMakeLists.txt:
add_subdirectory(third_party/TinyJSON)
target_link_libraries(your_target PUBLIC TinyJSON)You can automatically download and build TinyJSON using CMake's FetchContent:
include(FetchContent)
FetchContent_Declare(
TinyJSON
GIT_REPOSITORY https://github.com/FFMG/TinyJSON.git
GIT_TAG v0.2.2 # or main
)
FetchContent_MakeAvailable(TinyJSON)
target_link_libraries(your_target PUBLIC TinyJSON)Here is the absolute minimum code needed to parse a JSON string and read a value:
#include "TinyJSON.h"
#include <iostream>
int main() {
// 1. Parse a string
auto json = TinyJSON::TJ::parse(R"({"name": "TinyJSON", "fast": true})");
// 2. Read values
auto obj = dynamic_cast<TinyJSON::TJValueObject*>(json);
if (obj) {
std::cout << "Library: " << obj->get_string("name") << std::endl;
std::cout << "Fast? " << (obj->get_boolean("fast") ? "Yes" : "No") << std::endl;
}
// 3. Clean up
delete json;
return 0;
}- Clone this repository
- init the submodules
git submodule --init --recursive
The whole number ranges are +9223372036854775807 and -9223372036854775806
- Make sure that all the code follow our own coding standards.
- [] Update comments
- Finish documentation how to use the code.
- [] Run/Create some benchmark tests, make sure they are up to date.
- Arrays
- Objects
- Values
- More tests!
- Add version number (started at version 0.0.1)
- Copyright
- Add 2 kinds of error handling, either throw or return an error structure.
- [] While we do not care about size, (on paper), it would be nice to implement limits in the code.
- depth
- [] string
- [] numbers
- Automated build
- Run on linux/gcc/g++ or something other than visual studio.
g++ -std=c++11 -Wall -Wextra -Werror -O3 src/tinyJSON.cpp -o a.exe - We need to add copy and move constructors to
TJValueand the derived classes. - [] Add Macro(s) explanation like
TJ_INCLUDE_STD_STRINGfor example. - Add support for JSON5 https://json5.org 📄 https://github.com/json5/json5
The supported versions of c++ are c++ 11 and later.