yyjson: Introduction

yyjson 0.10.0 A high performance C JSON library.

Introduction

A high performance JSON library written in ANSI C.

# Features

- Fast: can read or write gigabytes per second JSON data on modern CPUs.
- Portable: complies with ANSI C (C89) for cross-platform compatibility.
- Strict: complies with RFC 8259 JSON standard, ensuring strict number format and UTF-8 validation.
- Extendable: offers options to allow comments, trailing commas, NaN/Inf, and custom memory allocator.
- Accuracy: can accurately read and write `int64`, `uint64`, and `double` numbers.
- Flexible: supports unlimited JSON nesting levels, `\u0000` characters, and non null-terminated strings.
- Manipulation: supports querying and modifying using JSON Pointer, JSON Patch and JSON Merge Patch.
- Developer-Friendly: easy integration with only one `h` and one `c` file.

# Limitations

- An array or object is stored as a data structure such as linked list, which makes accessing elements by index or key slower than using an iterator.
- Duplicate keys are allowed in an object, and the order of the keys is preserved.
- JSON parsing result is immutable, requiring a `mutable copy` for modification.

# Performance

Benchmark project and dataset: yyjson_benchmark

The simdjson's new `On Demand` API is faster if most JSON fields are known at compile-time. This benchmark project only checks the DOM API, a new benchmark will be added later.

#### AWS EC2 (AMD EPYC 7R32, gcc 9.3)

twitter.json | parse (GB/s) | stringify (GB/s)
--- | --- | ---
yyjson(insitu) | 1.80 | 1.51
yyjson | 1.72 | 1.42
simdjson | 1.52 | 0.61
sajson | 1.16 |
rapidjson(insitu) | 0.77 |
rapidjson(utf8) | 0.26 | 0.39
cjson | 0.32 | 0.17
jansson | 0.05 | 0.11

#### iPhone (Apple A14, clang 12)

twitter.json | parse (GB/s) | stringify (GB/s)
--- | --- | ---
yyjson(insitu) | 3.51 | 2.41
yyjson | 2.39 | 2.01
simdjson | 2.19 | 0.80
sajson | 1.74
rapidjson(insitu) | 0.75 |
rapidjson(utf8) | 0.30 | 0.58
cjson | 0.48 | 0.33
jansson | 0.09 | 0.24

More benchmark reports with interactive charts (update 2020-12-12)

Platform | CPU | Compiler | OS | Report
--- | --- | --- | --- | ---
Intel NUC 8i5 | Core i5-8259U | msvc 2019 | Windows 10 2004 | Charts
Intel NUC 8i5 | Core i5-8259U | clang 10.0 | Ubuntu 20.04 | Charts
Intel NUC 8i5 | Core i5-8259U | gcc 9.3 | Ubuntu 20.04 | Charts
AWS EC2 c5a.large | AMD EPYC 7R32 | gcc 9.3 | Ubuntu 20.04 | Charts
AWS EC2 t4g.medium | Graviton2 (ARM64) | gcc 9.3 | Ubuntu 20.04 | Charts
Apple iPhone 12 Pro | A14 (ARM64) | clang 12.0 | iOS 14 | Charts

### For better performance, yyjson prefers:

- A modern processor with:
  - high instruction level parallelism
  - excellent branch predictor
  - low penalty for misaligned memory access
- A modern compiler with good optimizer (e.g. clang)

# Sample Code

### Read JSON string

const char *json = "{\"name\":\"Mash\",\"star\":4,\"hits\":[2,2,1,3]}";
// Read JSON and get root
yyjson_doc *doc = yyjson_read(json, strlen(json), 0);
yyjson_val *root = yyjson_doc_get_root(doc);
// Get root["name"]
yyjson_val *name = yyjson_obj_get(root, "name");
printf("name: %s\n", yyjson_get_str(name));
printf("name length:%d\n", (int)yyjson_get_len(name));
// Get root["star"]
yyjson_val *star = yyjson_obj_get(root, "star");
printf("star: %d\n", (int)yyjson_get_int(star));
// Get root["hits"], iterate over the array
yyjson_val *hits = yyjson_obj_get(root, "hits");
size_t idx, max;
yyjson_val *hit;
yyjson_arr_foreach(hits, idx, max, hit) {
printf("hit%d: %d\n", (int)idx, (int)yyjson_get_int(hit));
}
// Free the doc
yyjson_doc_free(doc);
// All functions accept NULL input, and return NULL on error.
yyjson_obj_get
yyjson_api_inline yyjson_val * yyjson_obj_get(yyjson_val *obj, const char *key)
Definition: yyjson.h:5174
yyjson_get_int
yyjson_api_inline int yyjson_get_int(yyjson_val *val)
Definition: yyjson.h:4973
yyjson_arr_foreach
#define yyjson_arr_foreach(arr, idx, max, val)
Definition: yyjson.h:1884
yyjson_get_str
yyjson_api_inline const char * yyjson_get_str(yyjson_val *val)
Definition: yyjson.h:4985
yyjson_doc_get_root
yyjson_api_inline yyjson_val * yyjson_doc_get_root(yyjson_doc *doc)
Definition: yyjson.h:4839
yyjson_doc_free
yyjson_api_inline void yyjson_doc_free(yyjson_doc *doc)
Definition: yyjson.h:4851
yyjson_get_len
yyjson_api_inline size_t yyjson_get_len(yyjson_val *val)
Definition: yyjson.h:4989
yyjson_read
yyjson_api_inline yyjson_doc * yyjson_read(const char *dat, size_t len, yyjson_read_flag flg)
Definition: yyjson.h:954
yyjson_doc
Definition: yyjson.h:4519
yyjson_val
Definition: yyjson.h:4514

### Write JSON string

// Create a mutable doc
yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
yyjson_mut_val *root = yyjson_mut_obj(doc);
yyjson_mut_doc_set_root(doc, root);
// Set root["name"] and root["star"]
yyjson_mut_obj_add_str(doc, root, "name", "Mash");
yyjson_mut_obj_add_int(doc, root, "star", 4);
// Set root["hits"] with an array
int hits_arr[] = {2, 2, 1, 3};
yyjson_mut_val *hits = yyjson_mut_arr_with_sint32(doc, hits_arr, 4);
yyjson_mut_obj_add_val(doc, root, "hits", hits);
// To string, minified
const char *json = yyjson_mut_write(doc, 0, NULL);
if (json) {
printf("json: %s\n", json); // {"name":"Mash","star":4,"hits":[2,2,1,3]}
free((void *)json);
}
// Free the doc
yyjson_mut_doc_free(doc);
yyjson_mut_obj_add_val
yyjson_api_inline bool yyjson_mut_obj_add_val(yyjson_mut_doc *doc, yyjson_mut_val *obj, const char *key, yyjson_mut_val *val)
Definition: yyjson.h:7013
yyjson_mut_arr_with_sint32
yyjson_api_inline yyjson_mut_val * yyjson_mut_arr_with_sint32(yyjson_mut_doc *doc, const int32_t *vals, size_t count)
Definition: yyjson.h:5983
yyjson_mut_obj_add_int
yyjson_api_inline bool yyjson_mut_obj_add_int(yyjson_mut_doc *doc, yyjson_mut_val *obj, const char *key, int64_t val)
Definition: yyjson.h:6925
yyjson_mut_obj
yyjson_api_inline yyjson_mut_val * yyjson_mut_obj(yyjson_mut_doc *doc)
Definition: yyjson.h:6579
yyjson_mut_doc_free
yyjson_api void yyjson_mut_doc_free(yyjson_mut_doc *doc)
yyjson_mut_write
yyjson_api_inline char * yyjson_mut_write(const yyjson_mut_doc *doc, yyjson_write_flag flg, size_t *len)
Definition: yyjson.h:1361
yyjson_mut_doc_set_root
yyjson_api_inline void yyjson_mut_doc_set_root(yyjson_mut_doc *doc, yyjson_mut_val *root)
Definition: yyjson.h:5396
yyjson_mut_obj_add_str
yyjson_api_inline bool yyjson_mut_obj_add_str(yyjson_mut_doc *doc, yyjson_mut_val *obj, const char *key, const char *val)
Definition: yyjson.h:6945
yyjson_mut_doc_new
yyjson_api yyjson_mut_doc * yyjson_mut_doc_new(const yyjson_alc *alc)
yyjson_mut_doc
Definition: yyjson.h:5329
yyjson_mut_val
Definition: yyjson.h:5281

### Read JSON file with options

// Read JSON file, allowing comments and trailing commas
yyjson_read_flag flg = YYJSON_READ_ALLOW_COMMENTS | YYJSON_READ_ALLOW_TRAILING_COMMAS;
yyjson_read_err err;
yyjson_doc *doc = yyjson_read_file("/tmp/config.json", flg, NULL, &err);
// Iterate over the root object
if (doc) {
yyjson_val *obj = yyjson_doc_get_root(doc);
yyjson_obj_iter iter;
yyjson_obj_iter_init(obj, &iter);
yyjson_val *key, *val;
while ((key = yyjson_obj_iter_next(&iter))) {
val = yyjson_obj_iter_get_val(key);
printf("%s: %s\n", yyjson_get_str(key), yyjson_get_type_desc(val));
}
} else {
printf("read error (%u): %s at position: %ld\n", err.code, err.msg, err.pos);
}
// Free the doc
yyjson_doc_free(doc);
YYJSON_READ_ALLOW_TRAILING_COMMAS
static const yyjson_read_flag YYJSON_READ_ALLOW_TRAILING_COMMAS
Definition: yyjson.h:758
yyjson_obj_iter_init
yyjson_api_inline bool yyjson_obj_iter_init(yyjson_val *obj, yyjson_obj_iter *iter)
Definition: yyjson.h:5199
yyjson_obj_iter_get_val
yyjson_api_inline yyjson_val * yyjson_obj_iter_get_val(yyjson_val *key)
Definition: yyjson.h:5232
yyjson_read_flag
uint32_t yyjson_read_flag
Definition: yyjson.h:731
yyjson_get_type_desc
yyjson_api_inline const char * yyjson_get_type_desc(yyjson_val *val)
Definition: yyjson.h:4940
yyjson_read_err::code
yyjson_read_code code
Definition: yyjson.h:837
yyjson_obj_iter_next
yyjson_api_inline yyjson_val * yyjson_obj_iter_next(yyjson_obj_iter *iter)
Definition: yyjson.h:5222
yyjson_read_file
yyjson_api yyjson_doc * yyjson_read_file(const char *path, yyjson_read_flag flg, const yyjson_alc *alc, yyjson_read_err *err)
yyjson_read_err::pos
size_t pos
Definition: yyjson.h:841
yyjson_read_err::msg
const char * msg
Definition: yyjson.h:839
YYJSON_READ_ALLOW_COMMENTS
static const yyjson_read_flag YYJSON_READ_ALLOW_COMMENTS
Definition: yyjson.h:761
yyjson_obj_iter
Definition: yyjson.h:1952
yyjson_read_err
Definition: yyjson.h:835

### Write JSON file with options

// Read the JSON file as a mutable doc
yyjson_doc *idoc = yyjson_read_file("/tmp/config.json", 0, NULL, NULL);
yyjson_mut_doc *doc = yyjson_doc_mut_copy(idoc, NULL);
yyjson_mut_val *obj = yyjson_mut_doc_get_root(doc);
// Remove null values in root object
yyjson_mut_obj_iter iter;
yyjson_mut_obj_iter_init(obj, &iter);
yyjson_mut_val *key, *val;
while ((key = yyjson_mut_obj_iter_next(&iter))) {
val = yyjson_mut_obj_iter_get_val(key);
if (yyjson_mut_is_null(val)) {
yyjson_mut_obj_iter_remove(&iter);
}
}
// Write the json pretty, escape unicode
yyjson_write_flag flg = YYJSON_WRITE_PRETTY | YYJSON_WRITE_ESCAPE_UNICODE;
yyjson_write_err err;
yyjson_mut_write_file("/tmp/config.json", doc, flg, NULL, &err);
if (err.code) {
printf("write error (%u): %s\n", err.code, err.msg);
}
// Free the doc
yyjson_doc_free(idoc);
yyjson_mut_doc_free(doc);
yyjson_doc_mut_copy
yyjson_api yyjson_mut_doc * yyjson_doc_mut_copy(yyjson_doc *doc, const yyjson_alc *alc)
yyjson_mut_is_null
yyjson_api_inline bool yyjson_mut_is_null(yyjson_mut_val *val)
Definition: yyjson.h:5411
yyjson_write_err::msg
const char * msg
Definition: yyjson.h:1155
yyjson_mut_obj_iter_next
yyjson_api_inline yyjson_mut_val * yyjson_mut_obj_iter_next(yyjson_mut_obj_iter *iter)
Definition: yyjson.h:6513
yyjson_mut_obj_iter_remove
yyjson_api_inline yyjson_mut_val * yyjson_mut_obj_iter_remove(yyjson_mut_obj_iter *iter)
Definition: yyjson.h:6530
yyjson_mut_doc_get_root
yyjson_api_inline yyjson_mut_val * yyjson_mut_doc_get_root(yyjson_mut_doc *doc)
Definition: yyjson.h:5392
yyjson_mut_obj_iter_get_val
yyjson_api_inline yyjson_mut_val * yyjson_mut_obj_iter_get_val(yyjson_mut_val *key)
Definition: yyjson.h:6525
YYJSON_WRITE_ESCAPE_UNICODE
static const yyjson_write_flag YYJSON_WRITE_ESCAPE_UNICODE
Definition: yyjson.h:1094
yyjson_mut_write_file
yyjson_api bool yyjson_mut_write_file(const char *path, const yyjson_mut_doc *doc, yyjson_write_flag flg, const yyjson_alc *alc, yyjson_write_err *err)
yyjson_mut_obj_iter_init
yyjson_api_inline bool yyjson_mut_obj_iter_init(yyjson_mut_val *obj, yyjson_mut_obj_iter *iter)
Definition: yyjson.h:6488
yyjson_write_err::code
yyjson_write_code code
Definition: yyjson.h:1153
YYJSON_WRITE_PRETTY
static const yyjson_write_flag YYJSON_WRITE_PRETTY
Definition: yyjson.h:1091
yyjson_write_flag
uint32_t yyjson_write_flag
Definition: yyjson.h:1081
yyjson_mut_obj_iter
Definition: yyjson.h:3310
yyjson_write_err
Definition: yyjson.h:1151

# Documentation

The latest (unreleased) documentation can be accessed in the doc directory. The pre-generated Doxygen HTML for the release version can be viewed here:

- Home Page
  - Build and test
  - API and sample code
  - Data structure
  - Changelog

# Packaging status

# Built With yyjson

A non-exhaustive list of projects that expose yyjson to other languages or use yyjson internally for a major feature. If you have a project that uses yyjson, feel free to open a PR to add it to this list.

Project | Language | Description
--- | --- | ---
py_yyjson | Python | Python bindings for yyjson
orjson | Python | JSON library for Python with an optional yyjson backend
cpp-yyjson | C++ | C++ JSON library with a yyjson backend
reflect-cpp | C++ | C++ library for serialization through automated field name retrieval from structs
yyjsonr | R | R binding for yyjson
Ananda | Swift | JSON model decoding based on yyjson
duckdb | C++ | DuckDB is an in-process SQL OLAP Database Management System
fastfetch | C | A neofetch-like tool for fetching system information and displaying them in a pretty way
Zrythm | C | Digital Audio Workstation that uses yyjson to serialize JSON project files
bemorehuman | C | Recommendation engine with a focus on uniqueness of the person receiving the rec

# TODO for v1.0

- [x] Add documentation page.
- [x] Add GitHub workflow for CI and codecov.
- [x] Add more tests: valgrind, sanitizer, fuzzing.
- [x] Support JSON Pointer to query and modify JSON.
- [x] Add `RAW` type for JSON reader and writer.
- [ ] Add option to limit real number output precision.
- [ ] Add option to support JSON5 (if feasible).
- [ ] Add functions to diff two JSON documents.
- [ ] Add documentation on performance optimizations.
- [ ] Ensure ABI stability.

# License

This project is released under the MIT license.