概要
ANSI Cで書かれた高性能なJSONライブラリです。
特徴
- 高速:現代のCPUでは、毎秒数ギガバイトのJSONデータを読み書きできます。
- 移植性:ANSI C(C89)に準拠し、SIMDを明示的には使用しません。
- 厳密:RFC 8259のJSON標準に準拠し、数値形式の厳密な処理とUTF-8の検証を行います。
- 拡張性:JSON5の各機能や独自アロケーターを有効にするオプションを用意しています。
- 正確:
int64、uint64、doubleの数値を正確に読み書きできます。 - 柔軟性:JSONのネスト段数に制限がなく、
\u0000文字やNUL終端されていない文字列に対応します。 - 操作:JSON Pointer、JSON Patch、JSON Merge Patchを使った検索と変更に対応します。
- 開発者に使いやすい:
.hファイル1つと.cファイル1つだけで簡単に組み込めます。
制限
- 配列やオブジェクトは、連結リストのようなデータ構造で格納されます。そのため、インデックスやキーによる要素へのアクセスは、イテレーターを使う場合より遅くなります。
- オブジェクトのキーは重複を許容し、キーの順序は保持されます。
- JSONの解析結果は不変です。変更するには可変のコピーを作る必要があります。
性能
ベンチマーク用のプロジェクトとデータセット:yyjson_benchmark
JSONのフィールドの大半がコンパイル時に分かっている場合、simdjsonの新しいOn Demand APIのほうが高速です。
このベンチマーク用プロジェクトはDOM APIだけを検査しています。新しいベンチマークは今後追加する予定です。
AWS EC2(AMD EPYC 7R32、gcc 9.3)
| twitter.json | 解析(GB/s) | 文字列化(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 | 解析(GB/s) | 文字列化(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 |
対話型グラフを含む、その他のベンチマークレポート(最終更新日:2020-12-12)
| プラットフォーム | CPU | コンパイラー | OS | レポート |
|---|---|---|---|---|
| Intel NUC 8i5 | Core i5-8259U | msvc 2019 | Windows 10 2004 | グラフ |
| Intel NUC 8i5 | Core i5-8259U | clang 10.0 | Ubuntu 20.04 | グラフ |
| Intel NUC 8i5 | Core i5-8259U | gcc 9.3 | Ubuntu 20.04 | グラフ |
| AWS EC2 c5a.large | AMD EPYC 7R32 | gcc 9.3 | Ubuntu 20.04 | グラフ |
| AWS EC2 t4g.medium | Graviton2 (ARM64) | gcc 9.3 | Ubuntu 20.04 | グラフ |
| Apple iPhone 12 Pro | A14 (ARM64) | clang 12.0 | iOS 14 | グラフ |
より良い性能を得るために、yyjsonが適している環境
- 次の特性を持つ現代のプロセッサー:
- 命令レベルの並列性が高い
- 分岐予測が優れている
- メモリアクセスがアラインメントに沿わない場合のペナルティーが小さい
- 優れた最適化器を備えた現代のコンパイラー(clangなど)
サンプルコード
JSON文字列を読み込む
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.JSON文字列を書き出す
// 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);オプションを指定してJSONファイルを読み込む
// 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);オプションを指定してJSONファイルを書き出す
// 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 with pretty printing, 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);ドキュメント
最新の(未リリースの)文書はdocディレクトリで参照できます。 リリース版について事前生成されたDoxygen HTMLは、次の場所で閲覧できます。
パッケージ提供状況
yyjsonを使って作られたもの
yyjsonをほかの言語から使えるようにするプロジェクトや、主要な機能の内部でyyjsonを使うプロジェクトの、網羅的ではない一覧です。 yyjsonを使うプロジェクトをお持ちでしたら、一覧への追加を提案するPRを気軽に送ってください。
| プロジェクト | 言語 | 説明 |
|---|---|---|
| ssrJSON | Python | yyjsonを基盤とする、SIMDで高速化された高性能かつ正確なPythonのJSON解析ライブラリ |
| py_yyjson | Python | yyjsonのPythonバインディング |
| orjson | Python | yyjsonのバックエンドを任意で使えるPython用JSONライブラリ |
| serin | C++ / Python | TOON、JSON、YAMLに対応し、形式をまたいで変換できるC++・Pythonのシリアライズライブラリ |
| cpp-yyjson | C++ | yyjsonのバックエンドを持つC++用JSONライブラリ |
| reflect-cpp | C++ | 構造体からフィールド名を自動取得してシリアライズするC++ライブラリ |
| xyjson | C++ | 便利な演算子オーバーロードを備えた、yyjsonのC++プロキシーおよびラッパー |
| yyjsonr | R | yyjsonのRバインディング |
| Ananda | Swift | yyjsonを用いたJSONモデルのデコード |
| ReerJSON | Swift | JSONDecoderに代わる、より高速なもの |
| swift-yyjson | Swift | yyjsonを基盤とする高速なSwift用JSONライブラリ |
| duckdb | C++ | プロセス内で動作するSQL OLAPデータベース管理システム |
| fastfetch | C | システム情報を見やすく表示する、Cで書かれたneofetchのようなツール |
| Zrythm | C | JSONプロジェクトファイルのシリアライズにyyjsonを使うデジタルオーディオワークステーション |
| bemorehuman | C | 推薦を受ける人の固有性を重視する推薦エンジン |
| mruby-yyjson | mruby | mruby向けの、yyjsonを使った効率的なJSON解析とシリアライズ |
| YYJSON.jl | Julia | yyjsonのJuliaバインディング |
| yyjson-go | Go | Goへ変換されたyyjson。CGoを使わず、2〜4倍高速 |
| nim-yyjson | Nim | yyjsonの薄いNimバインディング |
v1.0に向けたTODO
- ドキュメントページを追加する。
- CIとcodecov用のGitHub workflowを追加する。
- valgrind、sanitizer、fuzzingなどのテストを増やす。
- 検索・変更のためのJSON Pointerに対応する。
- JSONのリーダーとライターに
RAW型を追加する。 - 実数の出力精度を制限するオプションを追加する。
- JSON5をサポートするオプションを追加する。
- ストリーミングJSON APIを追加する。
- 2つのJSONドキュメントの差分を求める関数を追加する。
- 性能最適化についての文書を追加する。
- ABIの安定性を確保する。
ライセンス
このプロジェクトはMITライセンスで公開されています。