glwjson [](https://coveralls.io/github/yak32/glwjson) [](https://opensource.org/licenses/BSD-3-Clause)

June 14, 2026 · View on GitHub

Header-only, cross-platform and compact JSON serialization for C++ with no heap allocations for property parsing. Parses any standard JSON by default.

Usage

Just drop the header (glw_json.h) into your C++ project and include it into a source file

#include "glw_json.h"

License

Licensed under BSD to simplify the usage.

How to build and run tests

Install cmake

git clone https://github.com/yak32/glw_json.git
cd glw_json
git submodule update --init
mkdir build
cd build
cmake ..
cmake --build . 
 ./../tests/tests

(use Git Bash on Windows)

Example

#include "glw_json.h"

using namespace json;

struct vec3{
  float x,y,z;
};

template<typename T>
bool serialize(T& t, vec3& v){
	SERIALIZE(x);
	SERIALIZE(y);
	SERIALIZE(z);
	return true;
}

int main(){

	vec3 v;
	if (load_object_from_file("test.json", v) != JSON_OK)
		return 0;

	if (save_object_to_file("test2.json", v) != JSON_OK)
		return 0;

	return 1;
}

Options

The load_object_from_* functions take an options bitmask. The default is DEFAULT_OPTIONS = RESET_IF_ABSENT | PARSE_ANY:

  • PARSE_ANY (default) — parse any standard JSON: properties may appear in any order, the JSON may carry properties the struct does not read, and serialize() may list its fields in any order. Each field is found by binary search over the sorted property map.
  • (omit PARSE_ANY) — a faster sorted linear merge for trusted, clean data. It requires that (a) serialize() lists fields in ascending name order and (b) every JSON property is consumed by the struct; otherwise loading fails. (The initial hash-map implementation was ~2x slower; the sorted merge avoids per-property allocations.) To sort the fields inside a serialize() body in Sublime Text 2/3, select its contents and press F9.
  • RESET_IF_ABSENT (default) — a field missing from the JSON is reset to V().
  • ERROR_IF_ABSENT — a missing field makes loading fail.

A malformed value (wrong type or bad syntax) always fails and returns the offending line number, in either parsing mode.

Example: reading a subset of a larger JSON (the default)

The serialize() object parameter must be named v — that is the name the SERIALIZE(field) macro expands to (t.process("field", v.field)).

struct point { float x, y; };                 // fields may be listed in any order under PARSE_ANY
template <typename T> bool serialize(T& t, point& v) {
	return SERIALIZE(x) && SERIALIZE(y);
}

// the JSON carries extra "label" and "z" that the struct ignores — it still parses:
const char* json = R"({"label":"p0","x":1.5,"y":-2.0,"z":9.0})";
point pt;
bool ok = (load_object_from_string(json, pt) == JSON_OK);  // ok == true, pt == {1.5, -2.0}

Dependencies

STL, c-runtime.

Performance

JSON loading - allocation-free parsing, N Log(N) performance where N is number of properties in a JSON object. Parsing speed is comparable to RapidJSON, saving is slower.

Saving to JSON - linear performance, std::ofstream is used to save data ( some allocations )

benchmark.cpp contains some logic to compare performance with RapidJson (currently parsing and loading only). Current results:

Run on 10 year old PC (8 X 3342 MHz CPU s) - glw_json is 25% slower than Rapid JSON for parsing:

BenchmarkTimeCPUIterations
BM_glw_json_load51367 ns51618 ns11200
BM_glw_json_save306253 ns299944 ns2240
BM_rapid_json_load40850 ns40981 ns17920

Tests

Run cmake on CMakeLists.txt in tests folder. Tests require C++11 support to compile (glw_json.h doesn't).

Compartibility

Tested with Visual Studio 2013, 2015, GCC 4.3.6 to GCC 7.0 (HEAD), Clang 3.0 to 3.9.0 (trunc), compartible with C++98, C++11 and C++14.

More examples

#include "glw_json.h"
#include <sstream>

using namespace json;
using namespace std;

struct array_int_var {
	std::vector<int> val;
};
template <typename T> bool serialize(T& t, array_int_var& v) {
	return SERIALIZE(val);
}

struct obj_array_var1 {
	int val1;
	float val2;
};

struct obj_array_var2 {
	int val1;
	float val2;
	vector<obj_array_var1> val3;
};

struct object_pointer_in_object {
	int val1;
	float val2;
	obj_array_var1* val3;
};

template <typename T> bool serialize(T& t, obj_array_var1& v) {
	bool b = true;
	b &= SERIALIZE(val1);
	b &= SERIALIZE(val2);
	return b;
}
template <typename T> bool serialize(T& t, obj_array_var2& v) {
	bool b = true;
	b &= SERIALIZE(val1);
	b &= SERIALIZE(val2);
	b &= SERIALIZE(val3);
	return b;
}

template <typename T> bool serialize(T& t, object_pointer_in_object& v) {
	bool b = true;
	b &= SERIALIZE(val1);
	b &= SERIALIZE(val2);
	b &= SERIALIZE(val3);
	return b;
}

int main(){
	vec3 v;

	// array
	stringstream ss;
	array_int_var av;
	av.val = {1, 2, 3};

	if (save_object_to_stream(av, ss) != JSON_OK )
		printf("failed");

	// array of objects
	stringstream sss;
	obj_array_var2 oav;
	oav.val1 = 1;
	oav.val2 = 1.1f;
	oav.val3.resize(1);
	oav.val3[0].val1 = 1;
	oav.val3[0].val2 = 1.1f;

	if (JSON_OK != save_object_to_stream(oav, sss))
		printf("failed");

	object_pointer_in_object obj_in_obj;
	if (JSON_OK != load_object_from_file("object in object test", obj_in_obj))
		printf("failed");

	return 1;
}