Skip to content

FFMG/tinyjson

Repository files navigation

TinyJSON

CI License: MIT Version

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.

Contents

Supported JSON

See more in the specifications document.

JSON5 Support

TinyJSON is a fully compliant JSON5 parser. JSON5 is a superset of JSON that makes it easier for humans to write and maintain.

Key differences between JSON5 and standard JSON

  • 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 for NaN and Infinity. Can start with an explicit plus sign (+42).
  • Comments: Support for single-line (//) and multi-line (/* ... */) comments.
  • Whitespace: Support for additional Unicode whitespace characters.

Parsing JSON5 Example

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;
}

Data types

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)

Simple examples

Version Control

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";

Options

Parse Options

  • 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 (like get_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
}

Write Options

  • 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?
}

Exceptions

Parsing Exception

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
  }

Write Exception

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
  }

Check if JSON is valid

You can simply call the TinyJSON::TJ::is_valid(...) method

    if(TinyJSON::TJ::is_valid("[123,456]"))
    {
      // do this
    }
    else
    {
      // do that
    }

Read a JSON file

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.

Read a JSON string

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;
...

Search an object by key

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;

Check if key exists

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

How to create a JSON 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->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

Write a JSON string

  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;

UTF-8 DOM

Regardless if you are parsing a file or parsing a string, TinyJSON will parse the file for UTF-8 DOM if there is one.

Using a JSON string

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::minify

Objects

Each 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;

Adding values to an object

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;

Arrays

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;

Adding values to an arrays

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;

Removing values from an array

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;

Data type

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;

Iterate values

  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;

Get values

Generic Get values

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");            // true

Get values with default

The 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>(); // 42

The 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");

Strict Get values

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
}

Set values

Generic Set values

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 double

The 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>

Strict Set values

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");

Getting Started

TinyJSON is designed to be as simple to integrate as possible.

1. Simple Drop-in

You can simply copy src/TinyJSON.h and src/TinyJSON.cpp directly into your project.

2. CMake add_subdirectory

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)

3. CMake FetchContent

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)

Quick Start Example

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;
}

Building and testing the project

  • Clone this repository
  • init the submodules git submodule --init --recursive

Data Range

Numbers

The whole number ranges are +9223372036854775807 and -9223372036854775806

TODO

  • 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 TJValue and the derived classes.
  • [] Add Macro(s) explanation like TJ_INCLUDE_STD_STRING for example.
  • Add support for JSON5 https://json5.org 📄 https://github.com/json5/json5

Supported CPP

The supported versions of c++ are c++ 11 and later.

About

TinyJSON: A high-performance, zero-dependency C++11 library for RFC 8259 and JSON5. Engineered for speed and minimal memory footprint.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors