{"id":38525259,"url":"https://github.com/yosh-matsuda/cpp-yyjson","last_synced_at":"2026-01-17T06:46:00.217Z","repository":{"id":147793212,"uuid":"583512183","full_name":"yosh-matsuda/cpp-yyjson","owner":"yosh-matsuda","description":"Ultra-fast and intuitive C++ JSON reader/writer with yyjson backend","archived":false,"fork":false,"pushed_at":"2025-05-01T02:38:43.000Z","size":180,"stargazers_count":73,"open_issues_count":2,"forks_count":9,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-05-01T03:19:23.429Z","etag":null,"topics":["cpp","cpp20","deserialization","header-only","json","serialization","yyjson"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yosh-matsuda.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-12-30T02:03:24.000Z","updated_at":"2025-05-01T02:34:58.000Z","dependencies_parsed_at":null,"dependency_job_id":"256afd39-f4a4-4eb9-ab84-9b4e61924d5b","html_url":"https://github.com/yosh-matsuda/cpp-yyjson","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/yosh-matsuda/cpp-yyjson","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yosh-matsuda%2Fcpp-yyjson","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yosh-matsuda%2Fcpp-yyjson/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yosh-matsuda%2Fcpp-yyjson/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yosh-matsuda%2Fcpp-yyjson/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yosh-matsuda","download_url":"https://codeload.github.com/yosh-matsuda/cpp-yyjson/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yosh-matsuda%2Fcpp-yyjson/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28502871,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T04:31:57.058Z","status":"ssl_error","status_checked_at":"2026-01-17T04:31:45.816Z","response_time":85,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp","cpp20","deserialization","header-only","json","serialization","yyjson"],"created_at":"2026-01-17T06:46:00.135Z","updated_at":"2026-01-17T06:46:00.198Z","avatar_url":"https://github.com/yosh-matsuda.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cpp-yyjson\n\nUltra-fast and intuitive C++ JSON reader/writer with yyjson backend.\n\n[![CI](https://github.com/yosh-matsuda/cpp-yyjson/actions/workflows/tests.yml/badge.svg)](https://github.com/yosh-matsuda/cpp-yyjson/actions/workflows/tests.yml)\n\n1.  [Features](#features)\n2.  [Requirements](#requirements)\n3.  [Overview](#overview)\n    1.  [JSON Reader](#json-reader)\n    2.  [JSON Writer](#json-writer)\n    3.  [Serialization and Deserialization](#serialization-and-deserialization)\n4.  [Installation](#installation)\n    1.  [Using CMake](#using-cmake)\n5.  [Benchmark](#benchmark)\n    1.  [Read performance](#read-performance)\n    2.  [Write performance](#write-performance)\n6.  [Reference](#reference)\n    1.  [Namespaces](#namespaces)\n    2.  [Immutable JSON classes](#immutable-json-classes)\n    3.  [Mutable JSON classes](#mutable-json-classes)\n    4.  [Allocator classes](#allocator-classes)\n    5.  [Serialize and deserialize JSON](#serialize-and-deserialize-json)\n    6.  [Performance best practices](#performance-best-practices)\n    7.  [Misc](#misc)\n7.  [Author](#author)\n\n## Features\n\n*   Header-only\n*   C++20 range adaption\n*   STL-like accessors\n*   Intuitive JSON construction\n*   Mutual transformation of JSON and C++ classes with\n    *   compile-time reflection of struct/class field name\n    *   pre-defined STL casters\n    *   user-defined casters in two ways\n*   Minimum overhead compared to yyjson\n*   Object lifetime safety\n\n## Requirements\n\n*   C++20 compiler with range supports\n    *   LLVM \u003e= 16\n    *   GCC \u003e= 12\n    *   clang-cl \u003e= 17 (Windows)\n    *   Visual Studio \u003e= 2022 version 17.5 (experimental)\n        *   ❌️ MSVC 19.36.32546.0\n        *   ✅ MSVC 19.38.33141.0\n        *   ✅ MSVC 19.39.33523.0\n        *   ✅ MSVC 19.40.33816.0\n        *   ❌️ MSVC 19.41.34123.0\n        *   ❌️ MSVC 19.43.34808.0\n*   [yyjson](https://github.com/ibireme/yyjson)\n*   [{fmt}](https://github.com/fmtlib/fmt)\n*   [Nameof C++](https://github.com/Neargye/nameof)\n\n## Overview\n\nThe following is an overview of reading and writing JSON using cpp-yyjson. See the [reference](#reference) for details.\n\n### JSON Reader\n\n```cpp\n#include \"cpp_yyjson.hpp\"\n\nusing namespace yyjson;\n\nauto json_str = R\"(\n{\n    \"id\": 1,\n    \"pi\": 3.141592,\n    \"name\": \"example\",\n    \"array\": [0, 1, 2, 3, 4],\n    \"currency\": {\n        \"USD\": 129.66,\n        \"EUR\": 140.35,\n        \"GBP\": 158.72\n    },\n    \"success\": true\n})\";\n\n// Read JSON string\nauto val = read(json_str);\n\n// as_xxx methods return std::optional\u003cT\u003e\nauto obj = *val.as_object();\n\n// Key access to the JSON object class\nauto id = *obj[\"id\"].as_int();\nauto pi = *obj[\"pi\"].as_real();\nauto name = *obj[\"name\"].as_string();\nauto success = *obj[\"success\"].as_bool();\n\n// JSON array/object classes adapt the range concept\nauto list = *obj[\"array\"].as_array();\nfor (const auto\u0026 v : list)\n{\n    // `write` returns JSON read-only string\n    std::cout \u003c\u003c v.write() \u003c\u003c std::endl;\n}\n\n// The range value type of object class is a key-value pair\nauto dict = *obj[\"currency\"].as_object();\nfor (const auto\u0026 [k, v] : dict)\n{\n    std::cout \u003c\u003c \"{\" \u003c\u003c k \u003c\u003c \": \" \u003c\u003c v.write() \u003c\u003c \"}\" \u003c\u003c std::endl;\n}\n\n// JSON array/object to container conversion\nauto numbers = cast\u003cstd::vector\u003cint\u003e\u003e(list);\nauto currency = cast\u003cstd::map\u003cstd::string_view, double\u003e\u003e(dict);\n\n// Stringify read-only string\nstd::cout \u003c\u003c obj.write() \u003c\u003c std::endl;\n// -\u003e {\"id\":1,\"pi\":3.141592,\"name\":\"example\",\"array\":[0,1,2,3,4],\n//     \"currency\":{\"USD\":129.66,\"EUR\":140.35,\"GBP\":158.72},\"success\":true}\n```\n\n### JSON Writer\n\n```cpp\n#include \"cpp_yyjson.hpp\"\n\nusing namespace yyjson;\n\n// Create a new JSON value from primitive types\nauto v_null = value();  // Initial value as null\nauto v_bool = value(true);\nauto v_num = value(3.141592);\nauto v_str = value(\"example\");\n\n// Create a new empty JSON array\nauto arr = array();\narr.emplace_back(1);\narr.emplace_back(\"string\");\n\n// Create a new empty JSON object\nauto obj = object();\nobj.emplace(\"USD\", 129.66);\nobj.emplace(\"date\", \"Wed Feb 1 2023\");\n\n// Conversion from range to JSON array class\nauto vec = std::vector{1, 2, 3};\nauto vec_nst = std::vector\u003cstd::vector\u003cint\u003e\u003e{{1, 2}, {3, 4}};\nauto arr_vec = array(vec);      // -\u003e [1,2,3]\nauto arr_nst = array(vec_nst);  // -\u003e [[1,2],[3,4]]\narray arr_rng =     // transformation via range adaptors\n    std::vector{1, 2, 3} | std::ranges::views::transform([](auto x) { return x * x; });\n    // -\u003e [1,4,9]\n\n// Conversion from key-value-like range to JSON object class\nauto kv_map =\n    std::map\u003cstd::string_view, double\u003e{{\"first\", 1.0}, {\"second\", 2.0}, {\"third\", 3.0}};\nauto val_map =\n    std::map\u003cstd::string_view, value\u003e{{\"number\", 1.5}, {\"error\", nullptr}, {\"text\", \"abc\"}};\nauto obj_map = object(kv_map);\nauto obj_kv  = object(val_map);\n\n// Construction by std::initializer_list\nauto init_arr = array{nullptr, true, \"2\", 3.0, {4.0, \"5\", false}, {{\"7\", 8}, {\"9\", {0}}}};\nauto init_obj = object{{\"id\", 1},\n                       {\"pi\", 3.141592},\n                       {\"name\", \"example\"},\n                       {\"array\", {0, 1, 2, 3, 4}},\n                       {\"currency\", {{\"USD\", 129.66}, {\"EUR\", 140.35}, {\"GBP\", 158.72}}},\n                       {\"success\", true}};\n```\n\n### Serialization and Deserialization\n\nAs shown above, cpp-yyjson provides conversion between JSON value/array/object classes and C++ ranges and container types recursively. In addition to that, the following additional JSON casters are available (see the [reference](#serialize-and-deserialize-json) in detail):\n\n*   Pre-defined STL casters (e.g., `std::optional`, `std::variant`, `std::tuple` ([C++23 tuple-like](https://wg21.link/P2165R4))).\n*   Conversion using compile-time reflection of struct/class if it is available.\n*   Registration of field names with `VISITABLE_STRUCT` macro.\n*   User-defined casters.\n\n#### Pre-defined STL casters\n\n```cpp\n// cast JSON value from/to std::optional\nauto nullable = std::optional\u003cint\u003e(3);\nauto serialized = value(nullable);                        // serialize std::optional to JSON value\nauto deserialized = cast\u003cdecltype(nullable)\u003e(serialized); // deserialize JSON value into std::optional\n\n// cast JSON value from/to std::variant\nauto variant = std::variant\u003cstd::monostate, int, std::string\u003e(\"example\");\nauto serialized = value(variant);                         // serialize std::variant to JSON value\nauto deserialized = cast\u003cdecltype(variant)\u003e(serialized);  // deserialize JSON value into std::variant\n\n// cast JSON array class from/to tuple-like array type\nauto tpl_arr = std::tuple{nullptr, true, \"2\", 3.0, std::tuple{4.0, \"5\", false}};\nauto serialized = array(tpl_arr);                         // serialize tuple-like array to JSON array\nauto deserialized = cast\u003cdecltype(tpl_arr)\u003e(serialized);  // deserialize JSON array into tuple-like\n\n// cast JSON object class from/to tuple-like object type\nstd::tuple tpl_obj = {std::pair{\"number\", 1.5}, std::pair{\"error\", nullptr}, std::pair{\"text\", \"abc\"}};\nauto serialized = object(tpl_obj);                        // serialize tuple-like object to JSON object\nauto deserialized = cast\u003cdecltype(tpl_obj)\u003e(serialized);  // deserialize JSON object into tuple-like\n```\n\n#### Automatic casting from/to JSON object with compile-time reflection\n\n(see the [reference](#serialize-and-deserialize-json) about available conditions)\n\n```cpp\nstruct X\n{\n    int a;\n    std::optional\u003cdouble\u003e b;\n    std::string c = \"default\";\n};\n\n// serialize struxt X to JSON object with field-name reflection\nauto reflectable = X{.a = 1, .b = std::nullopt, .c = \"x\"};\nauto serialized = object(visitable);\n// -\u003e {\"a\":1,\"b\":null,\"c\":\"x\"}\n\n// deserialize JSON object into struct X with field-name reflection\nauto deserialized = cast\u003cX\u003e(serialized);\n// -\u003e X{.a = 1, .b = std::nullopt, .c = \"x\"}\n```\n\nField name registration with `VISITABLE_STRUCT` macro:\n\n```cpp\n// register fields except `c` on purpose\nVISITABLE_STRUCT(X, a, b);\n\n// serialize visitable struxt X to JSON object\nauto visitable = X{.a = 1, .b = std::nullopt, .c = \"x\"};\nauto serialized = object(visitable);\n// -\u003e {\"a\":1,\"b\":null}\n\n// deserialize JSON object into struct X\nauto deserialized = cast\u003cX\u003e(serialized);\n// -\u003e X{.a = 1, .b = std::nullopt, .c = \"default\"}\n```\n\n#### User-define caster (see the [reference](#serialize-and-deserialize-json) in detail)\n\n```cpp\ntemplate \u003c\u003e\nstruct yyjson::caster\u003cX\u003e\n{\n    // convert X to string (serialize)\n    inline static auto to_json(const X\u0026 x)\n    {\n        return fmt::format(\"{} {} {}\", x.a, (x.b ? fmt::format(\"{}\", *y.b) : \"null\"), x.c);\n    }\n};\n\n// convert struxt X to JSON string with user-defined caster\nauto  = X{.a = 1, .b = std::nullopt, .c = \"x\"};\nauto serialized = value(visitable);\n// -\u003e \"1 null x\"\n```\n\n## Installation\n\nTo use cpp-yyjson, the dependent packages are required to be installed. It is convenient to use [vcpkg](https://github.com/microsoft/vcpkg) to install the packages:\n\n```bash\n$ ./vcpkg install yyjson fmt nameof\n```\n\nThen add the path `include/cpp_yyjson.hpp` to the include directory of your project.\n\n### Using CMake\n\nTo integrate cpp-yyjson into your CMake project, simply add the following:\n\n```cmake\nadd_subdirectory(\u003cPATH_TO_CLONE_DIR\u003e/cpp-yyjson ${CMAKE_CURRENT_BINARY_DIR}/cpp-yyjson)\ntarget_link_libraries(${PROJECT_NAME} PRIVATE cpp_yyjson::cpp_yyjson)\n```\n\nIf you have installed cpp-yyjson via CMake, `find_package` command is enabled:\n\n```cmake\nfind_package(cpp_yyjson CONFIG REQUIRED)\ntarget_link_libraries(${PROJECT_NAME} PRIVATE cpp_yyjson::cpp_yyjson)\n```\n\n## Benchmark\n\nBenchmark results are described to compare the cpp-yyjson with other prominent fast C/C++ JSON libraries: [yyjson](https://github.com/ibireme/yyjson) v0.6.0, [simdjson](https://github.com/simdjson/simdjson) v3.0.1, [rapidjson](https://github.com/Tencent/rapidjson/) #232389d, and [nlohmann-json](https://github.com/nlohmann/json) v3.9.1.\n\nThe results are obtained on Ubuntu 22.04, INTEL Core i9-12900K with all E cores and HTT disabled, compiled with GCC 12.1.0. The benchmark programs are in the [`test`](https://github.com/yosh-matsuda/cpp-yyjson/tree/main/test) directory.\n\n### Read performance\n\nIn each library, the following options are there for reading JSON. By using the appropriate options for your use case, the best performance can be achieved.\n\n*In-situ parsing*\n: Modify the input JSON string during parsing. This can be used if the string is writable (and/or padded at the end) and can be discarded after parsing. Therefore, this method cannot be used for a given fixed-length read-only JSON string. Alternatively, you can copy the input string once and use in-situ parsing.  \nThe [yyjson](https://github.com/ibireme/yyjson) and [simdjson](https://github.com/simdjson/simdjson) require some padding at the end of the JSON string for in-situ parsing but the [rapidjson](https://github.com/Tencent/rapidjson/) does not. For the former libraries, the JSON string must be copied even if it is writable but has a fixed length. The [simdjson](https://github.com/simdjson/simdjson) has two methods for parsing, which are \"DOM\" and \"On Demand\". The \"On Demand\" approach seems to be faster than \"DOM\" but less flexible because it behaves as a forward iterator like a stream and can only receive padded JSON strings.\n\n*Single buffer*\n: Reuse a single pre-allocated buffer or a parser object for multiple parsing. This is suitable for tasks that repeatedly read multiple JSON strings, e.g. API servers.  \nThe [yyjson](https://github.com/ibireme/yyjson) can prepare a pre-allocated buffer and the maximum required size of the buffer can be estimated from the length of the JSON string. The allocator can be given in the same way for the [rapidjson](https://github.com/Tencent/rapidjson/), but we need to clear it explicitly after parsing because the buffer will probably not be released automatically (please let me know if I make a wrong manner). For the [simdjson](https://github.com/simdjson/simdjson), the parser object is reusable to minimize the new allocation cost. Reusing string objects when copying JSON strings for in-situ and padding can also be considered a single buffer.\n\nThe benchmarks were performed on each JSON library with all possible patterns with the above options. Classified by the following keywords.\n\n`(no mark)`\n: No option.\n\n`insitu`, `pad`\n: In-situ parsing is used/a padded string is input.\n\n`dom`, `ond_pad`\n: \"DOM\" and \"On Demand\" parsing for the [simdjson](https://github.com/simdjson/simdjson), respectively.\n\n`single`\n: Reuse the parsing and temporal object as much as possible.\n\n`copy`\n: Create a copy of the input string for in-situ parsing or padded string input.\n\nThe JSON datasets are from [yyjson_benchmark](https://github.com/ibireme/yyjson_benchmark#json-datasets). Measurements are the median time to parse and iterate all elements with 100 repetitions on [google benchmark](https://github.com/google/benchmark). The time unit is `ms` and the raw logs are available [here](https://github.com/yosh-matsuda/cpp-yyjson/blob/main/test/bench_read.log).\n\n\u003cimg src=\"https://user-images.githubusercontent.com/59041398/222045668-9c2d0d89-d204-4b72-aca0-0e48fe3b0675.png\" width=\"18%\"\u003e\u003c/img\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/59041398/222045694-d44e9bdd-b275-4ed5-83a3-7a3bd9b89da1.png\" width=\"18%\"\u003e\u003c/img\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/59041398/222045693-16288a45-2a4c-4fec-9e98-1dcb1290494f.png\" width=\"18%\"\u003e\u003c/img\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/59041398/222045689-d375a98a-9d7c-4803-951e-968c60005568.png\" width=\"18%\"\u003e\u003c/img\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/59041398/222045685-bb79ba1f-7dbe-4880-9a90-6048638c09ca.png\" width=\"18%\"\u003e\u003c/img\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/59041398/222045684-fdc40c6f-6cca-4b65-b1ee-24d2e0e1bd2d.png\" width=\"18%\"\u003e\u003c/img\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/59041398/222045682-e20e95e8-ed44-4bbe-a54b-c7cffdf4ba61.png\" width=\"18%\"\u003e\u003c/img\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/59041398/222045678-9183e40b-77ea-467b-9692-6d8025781be0.png\" width=\"18%\"\u003e\u003c/img\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/59041398/222045676-55fc581e-0471-4bfa-b1fd-b41fe38e2250.png\" width=\"18%\"\u003e\u003c/img\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/59041398/222045674-87c6f9db-1723-41ac-a57f-165d0e1b3068.png\" width=\"18%\"\u003e\u003c/img\u003e\n\nThe cpp-yyjson shows a very good read performance, as same as the original [yyjson](https://github.com/ibireme/yyjson). A small overhead of cpp-yyjson compared to the [yyjson](https://github.com/ibireme/yyjson) may be from the pointer wrapped by `std::shared_ptr`.\n\n### Write performance\n\nThe write performance is measured by the time it takes to create a large array or object and output it as a JSON string. One option when creating a JSON is to make a copy of the string or not. The [yyjson](https://github.com/ibireme/yyjson) and the [rapidjson](https://github.com/Tencent/rapidjson/) have such as option and make a small difference in speed. The results are obtained by the size of 1,000,000 elements with 100 repetitions on [google benchmark](https://github.com/google/benchmark). The time unit is `ms` and the raw logs are available [here](https://github.com/yosh-matsuda/cpp-yyjson/blob/main/test/bench_write.log).\n\n\u003cimg src=\"https://user-images.githubusercontent.com/59041398/222316469-2f96e1b0-2146-49df-8755-42db5b584ce2.png\" width=\"15%\"\u003e\u003c/img\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/59041398/222316493-deb3cee1-2b5c-461a-97e4-03ba2b97a192.png\" width=\"15%\"\u003e\u003c/img\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/59041398/222316491-da8624fc-0e1e-4210-b9c4-055d1a43f31c.png\" width=\"15%\"\u003e\u003c/img\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/59041398/222316489-2947938d-1efd-4b2c-b7bb-d46518d3903a.png\" width=\"15%\"\u003e\u003c/img\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/59041398/222316487-c9e21410-8199-4655-af1a-22e37aa075f5.png\" width=\"15%\"\u003e\u003c/img\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/59041398/222316483-210d7918-d54d-4a8c-a267-4235c78fb635.png\" width=\"15%\"\u003e\u003c/img\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/59041398/222316481-c8209bdd-7e1d-4070-9ee0-f279dab3dda7.png\" width=\"15%\"\u003e\u003c/img\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/59041398/222316479-64dbbee1-bb31-4c30-b560-f9501690fc97.png\" width=\"15%\"\u003e\u003c/img\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/59041398/222316477-cddef0e7-8771-4529-a0d0-7ee338dd543d.png\" width=\"15%\"\u003e\u003c/img\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/59041398/222316476-293d8081-d7f5-4f70-b0ea-013ac4bc7943.png\" width=\"15%\"\u003e\u003c/img\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/59041398/222316472-e1510447-fa5f-4a82-bbff-c22db3ef4f33.png\" width=\"15%\"\u003e\u003c/img\u003e\n\nThe cpp-yyjson and [yyjson](https://github.com/ibireme/yyjson) show excellent write performance. In some cases, the cpp-yyjson performs slightly better than the original [yyjson](https://github.com/ibireme/yyjson) because it implements an additional *range*-based conversion to a JSON array and object.\n\n## Reference\n\n### Namespaces\n\nThe `yyjson` namespace includes the following function and classes. Typically, you will start with them for reading and writing JSON.\n\n| Function       |                                                                                                            |\n| -------------- | ---------------------------------------------------------------------------------------------------------- |\n| `yyjson::read` | Read a JSON string and return an *immutable* JSON document class which is alias of `yyjson::reader::value` |\n\n| JSON Type        |                                                                |\n| ---------------- | -------------------------------------------------------------- |\n| `yyjson::value`  | *Mutable* JSON value class; alias of `yyjson::writer::value`   |\n| `yyjson::array`  | *Mutable* JSON array class; alias of `yyjson::writer::array`   |\n| `yyjson::object` | *Mutable* JSON object class; alias of `yyjson::writer::object` |\n\n| Allocator class                |                                                                    |\n| ------------------------------ | ------------------------------------------------------------------ |\n| `yyjson::dynamic_allocator`    | Dynamic memory allocator wrapper, available for yyjson \u003e= v0.8.0   |\n| `yyjson::pool_allocator`       | Fixed size memory allocator wrapper on the heap                    |\n| `yyjson::stack_pool_allocator` | Fixed size memory allocator wrapper on the stack                   |\n\nIn internal namespaces, the cpp-yyjson provides JSON value, array, object, reference, and iterator classes. Each internal `yyjson::reader` and `yyjson::writer` namespace defines *immutable* and *mutable* JSON classes, respectively. Although you rarely need to be aware of the classes provided in the internal namespaces, the *reference* classes are noted here.\n\nThe JSON value, array, and object classes have the corresponding *reference* (only for *mutable* classes) and *const reference* versions of them as shown later, such as `value_ref`, `const_value_ref`, `array_ref`, `const_array_ref`, and so on. The *reference* classes have member functions with almost the same signature as the normal versions. The difference between a normal `value` class and a `[const_]value_ref` class is whether they have their data ownership or not. The *(const) reference* JSON classes appear in return types of member functions of the JSON classes. This is to maximize performance by avoiding copying.\n\n\u003e [!NOTE]\n\u003e The *reference* class refers to data in the owner, so its lifetime should be noted.\n\n### Immutable JSON classes\n\nImmutable JSON classes defined in `yyjson::reader` namespace are derived from the `yyjson::read` function that reads a JSON text string. It may not be necessary to use these classes directly.\n\n#### `yyjson::read`\n\nRead a JSON string and return an *immutable* JSON value.\n\nSee the reference of yyjson for the information on [reader flags](https://ibireme.github.io/yyjson/doc/doxygen/html/md_doc__a_p_i.html#autotoc_md34).\n\n```cpp\nyyjson::reader::value read(std::string_view json_string, [std::size_t len,] [Allocator alc,] ReadFlag = ReadFlag::NoFlag);\nyyjson::reader::value read(const std::string\u0026 json_string, [std::size_t len,] [Allocator alc,] ReadFlag = ReadFlag::NoFlag);\nyyjson::reader::value read(const char* json_string, [std::size_t len,] [Allocator alc,] ReadFlag = ReadFlag::NoFlag);\nyyjson::reader::value read(std::string\u0026 json_string, [std::size_t len,] [Allocator alc,] ReadFlag = ReadFlag::NoFlag);\nyyjson::reader::value read(char* json_string, [std::size_t len,] [Allocator alc,] ReadFlag = ReadFlag::NoFlag);\n\nenum class yyjson::ReadFlag : yyjson_read_flag\n{\n    NoFlag = YYJSON_READ_NOFLAG,\n    ReadInsitu = YYJSON_READ_INSITU,\n    AllowTrailingCommas = YYJSON_READ_ALLOW_TRAILING_COMMAS,\n    AllowComments = YYJSON_READ_ALLOW_COMMENTS,\n    AllowInfAndNan = YYJSON_READ_ALLOW_INF_AND_NAN,\n    NumberAsRaw = YYJSON_READ_NUMBER_AS_RAW,\n    AllowInvalidUnicode = YYJSON_READ_ALLOW_INVALID_UNICODE,\n    BignumAsRaw = YYJSON_READ_BIGNUM_AS_RAW // for yyjson \u003e= v0.7.0\n};\n```\n\nThe `read` function takes a JSON string and returns an immutable JSON value. The function argument may optionally specify a string length and an allocator to be described [later](#allocator-classes). Otherwise, the length of the JSON string is determined and the yyjson default allocator is used.\n\nIf the read option has the [`ReadInsitu`](https://ibireme.github.io/yyjson/doc/doxygen/html/md_doc__a_p_i.html#autotoc_md34) flag, You must specify the JSON string as writable (`std::string\u0026` or `char*`) and its length. This writable string must be padded at least [`YYJSON_PADDING_SIZE`](https://ibireme.github.io/yyjson/doc/doxygen/html/yyjson_8h.html#abbe8e69f634b1a5a78c1dae08b88e0ef) bytes to the end. The length of the JSON string should be unpadded.\n\n#### `yyjson::reader::value`\n\nThe immutable JSON value class is returned from `yyjson::read` function.\n\n**Constructor**\n\n```cpp\nyyjson::reader::value() = delete;\nyyjson::reader::value(const yyjson::reader::value\u0026) = default;\nyyjson::reader::value(yyjson::reader::value\u0026\u0026) = default;\n```\n\n**Member function**\n\n```cpp\n// Check the type of JSON\nbool is_null() const;       // null\nbool is_true() const;       // true\nbool is_false() const;      // false\nbool is_bool() const;       // bool\nbool is_uint() const;       // std::uint64_t\nbool is_sint() const;       // std::int64_t\nbool is_int() const;        // std::uint64_t/std::int64_t\nbool is_real() const;       // double\nbool is_num() const;        // std::uint64_t/std::int64_t/double\nbool is_string() const;     // string\nbool is_array() const;      // array\nbool is_object() const;     // object\nbool is_container() const;  // array/object\n\n// Get the content of the JSON value\nstd::optional\u003cstd::nullptr_t\u003e as_null() const;\nstd::optional\u003cbool\u003e as_bool() const;\nstd::optional\u003cstd::uint64_t\u003e as_uint() const;\nstd::optional\u003cstd::int64_t\u003e as_sint() const;\nstd::optional\u003cstd::int64_t\u003e as_int() const;\nstd::optional\u003cdouble\u003e as_real() const;\nstd::optional\u003cdouble\u003e as_num() const;\nstd::optional\u003cstd::string_view\u003e as_string() const\u0026;\nstd::optional\u003cstd::string\u003e as_string() \u0026\u0026;\nstd::optional\u003cyyjson::reader::const_array_ref\u003e as_array() const\u0026;\nstd::optional\u003cyyjson::reader::array\u003e as_array() \u0026\u0026;\nstd::optional\u003cyyjson::reader::const_object_ref\u003e as_object() const\u0026;\nstd::optional\u003cyyjson::reader::object\u003e as_object() \u0026\u0026;\n\n// Cast\ntemplate\u003ctypename T\u003e\nT cast\u003cT\u003e() const;\ntemplate\u003ctypename T\u003e\nexplicit operator T() const;\n\n// Output JSON string\nyyjson::json_string write(WriteFlag write_flag = WriteFlag::NoFlag) const;\ntemplate \u003cyyjson_allocator Allocator\u003e\nyyjson::json_string write(Allocator alc, WriteFlag write_flag = WriteFlag::NoFlag) const;\n\nenum class yyjson::WriteFlag : yyjson_write_flag\n{\n    NoFlag = YYJSON_WRITE_NOFLAG,\n    Pretty = YYJSON_WRITE_PRETTY,\n    EscapeUnicode = YYJSON_WRITE_ESCAPE_UNICODE,\n    EscapeSlashes = YYJSON_WRITE_ESCAPE_SLASHES,\n    AllowInfAndNan = YYJSON_WRITE_ALLOW_INF_AND_NAN,\n    InfAndNanAsNull = YYJSON_WRITE_INF_AND_NAN_AS_NULL,\n    AllowInvalidUnicode = YYJSON_WRITE_ALLOW_INVALID_UNICODE,\n    PrettyTwoSpaces = YYJSON_WRITE_PRETTY_TWO_SPACES    // for yyjson \u003e= v0.7.0\n};\n```\n\nThe `write` function returns a read-only string which is inherited from `std::string_view`.\n\nSee the reference of yyjson for the information on [writer flags](https://ibireme.github.io/yyjson/doc/doxygen/html/md_doc__a_p_i.html#autotoc_md40).\n\n**Example**\n\nSee also [Overview](#json-reader).\n\n```cpp\nusing namespace yyjson;\n\nstd::string_view json_str = R\"(\n{\n    \"id\": 1,\n    \"pi\": 3.141592,\n    \"emoji\": \"🫠\",\n    \"array\": [0, 1, 2, 3, 4],\n    \"currency\": {\n        \"USD\": 129.66,\n        \"EUR\": 140.35,\n        \"GBP\": 158.72\n    },\n    \"success\": true\n})\";\n\nauto val = read(json_str);\nstd::cout \u003c\u003c val.write(WriteFlag::Pretty) \u003c\u003c std::endl;\n// {\n//     \"id\": 1,\n//     \"pi\": 3.141592,\n//     \"emoji\": \"🫠\",\n//     \"array\": [\n//         0,\n//         1,\n//         2,\n//         3,\n//         4\n//     ],\n//     \"currency\": {\n//         \"USD\": 129.66,\n//         \"EUR\": 140.35,\n//         \"GBP\": 158.72\n//     },\n//     \"success\": true\n// }\n```\n\n#### `yyjson::reader::array`, `yyjson::reader::const_array_ref`\n\nThe immutable JSON array class is created by the `value::as_array` member function. This class adapts `std::ranges::input_range` concept.\n\n**Constructor**\n\n```cpp\nyyjson::reader::array() = delete;\nyyjson::reader::array(const yyjson::reader::array\u0026) = default;\nyyjson::reader::array(yyjson::reader::array\u0026\u0026) = default;\nyyjson::reader::array(const yyjson::reader::value\u0026);\nyyjson::reader::array(yyjson::reader::value\u0026\u0026);\n\nyyjson::reader::const_array_ref() = delete;\nyyjson::reader::const_array_ref(const yyjson::reader::const_array_ref\u0026) = default;\nyyjson::reader::const_array_ref(yyjson::reader::const_array_ref\u0026\u0026) = default;\n```\n\n**Member function**\n\n```cpp\n// STL-like functions\nyyjson::reader::const_array_iter cbegin() const;\nyyjson::reader::const_array_iter begin() const;\nyyjson::reader::const_array_iter cend() const;\nyyjson::reader::const_array_iter end() const;\nyyjson::reader::const_value_ref front() const;\nyyjson::reader::const_value_ref back() const;\nyyjson::reader::const_value_ref operator[](std::size_t) const;\nstd::size_t size() const;\nbool empty() const;\n\n// Cast\ntemplate\u003ctypename T\u003e\nT cast\u003cT\u003e() const;\ntemplate\u003ctypename T\u003e\nexplicit operator T() const;\n\n// Output JSON string (inherited)\nyyjson::json_string write(WriteFlag write_flag = WriteFlag::NoFlag) const;\ntemplate \u003cyyjson_allocator Allocator\u003e\nyyjson::json_string write(Allocator alc, WriteFlag write_flag = WriteFlag::NoFlag) const;\n\n// Range concept\nstd::ranges::iterator_t\u003cyyjson::reader::const_array_ref\u003e -\u003e yyjson::reader::const_array_iter\nstd::ranges::range_value_t\u003cyyjson::reader::const_array_ref\u003e -\u003e yyjson::reader::const_value_ref\n```\n\n**Example**\n\nSee also [Overview](#json-reader).\n\n```cpp\nusing namespace yyjson;\n\nstd::string_view json_str = R\"([0, \"1\", 2.0])\";\nauto val = read(json_str);\nassert(val.is_array());\n\n// NOTE: Prior to C++23 (P2644R1), range-based for loop for temporal std::optional\n// instance requires initializer because std::optional\u003cT\u003e::value() returns T\u0026\u0026.\n// for (const auto\u0026 v : *val.as_array()) { ... }             // 💀 UB\nfor (const auto arr = *val.as_array(); const auto\u0026 v : arr)  // ✅ OK\n{\n    std::cout \u003c\u003c v.write() \u003c\u003c std::endl;\n}\n// 0\n// \"1\"\n// 2.0\n```\n\n#### `yyjson::reader::object`, `yyjson::reader::const_object_ref`\n\nThe immutable JSON object class is created by the `value::as_object` member function. This class adapts `std::ranges::input_range` concept.\n\n**Constructor**\n\n```cpp\nyyjson::reader::object() = delete;\nyyjson::reader::object(const yyjson::reader::object\u0026) = default;\nyyjson::reader::object(yyjson::reader::object\u0026\u0026) = default;\nyyjson::reader::object(const yyjson::reader::value\u0026);\nyyjson::reader::object(yyjson::reader::value\u0026\u0026);\n\nyyjson::reader::const_object_ref() = delete;\nyyjson::reader::const_object_ref(const yyjson::reader::const_object_ref\u0026) = default;\nyyjson::reader::const_object_ref(yyjson::reader::const_object_ref\u0026\u0026) = default;\n```\n\n**Member function**\n\n```cpp\n// STL-like functions\nyyjson::reader::const_object_iter cbegin() const;\nyyjson::reader::const_object_iter begin() const;\nyyjson::reader::const_object_iter cend() const;\nyyjson::reader::const_object_iter end() const;\nyyjson::reader::const_object_iter find(std::string_view key) const;     // Note: O(N)\nyyjson::reader::const_value_ref operator[](std::string_view key) const; // Note: O(N)\nstd::size_t size() const;\nbool empty() const;\nbool contains(std::string_view key) const;  // Note: O(N)\n\n// Cast\ntemplate\u003ctypename T\u003e\nT cast\u003cT\u003e() const;\ntemplate\u003ctypename T\u003e\nexplicit operator T() const;\n\n// Output JSON string (inherited)\nyyjson::json_string write(WriteFlag write_flag = WriteFlag::NoFlag) const;\ntemplate \u003cyyjson_allocator Allocator\u003e\nyyjson::json_string write(Allocator alc, WriteFlag write_flag = WriteFlag::NoFlag) const;\n\n// Range concept\nusing yyjson::reader::const_key_value_ref_pair = std::pair\u003cstd::string_view, yyjson::reader::const_value_ref\u003e;\nstd::ranges::iterator_t\u003cyyjson::reader::const_object_ref\u003e -\u003e yyjson::reader::const_object_iter\nstd::ranges::range_value_t\u003cyyjson::reader::const_object_ref\u003e -\u003e yyjson::reader::const_key_value_ref_pair\n```\n\n**Example**\n\nSee also [Overview](#json-reader).\n\n```cpp\nusing namespace yyjson;\n\nstd::string_view json_str = R\"({\"USD\": 129.66, \"EUR\": 140.35, \"GBP\": 158.72})\";\nauto val = read(json_str);\nassert(val.is_object());\n\nauto obj = *val.as_object();\nstd::cout \u003c\u003c *obj[\"USD\"].as_real() \u003c\u003c std::endl;\nstd::cout \u003c\u003c *obj[\"EUR\"].as_real() \u003c\u003c std::endl;\nstd::cout \u003c\u003c *obj[\"GBP\"].as_real() \u003c\u003c std::endl;\n// 129.66\n// 140.35\n// 158.72\n```\n\n### Mutable JSON classes\n\nMutable JSON classes are defined in `yyjson::writer` namespace. The following user-constructible classes are exported in the top `yyjson` namespace,\n\n```cpp\nusing yyjson::value = yyjson::writer::value;\nusing yyjson::array = yyjson::writer::array;\nusing yyjson::object = yyjson::writer::object;\n```\n\n#### `yyjson::value`\n\nThe mutable JSON value class `yyjson::value` constructs JSON values such as `null`, `bool`, `number`, `string`, `array`, and `object`.\n\n**Constructor**\n\n```cpp\n// Concepts (not defined in the library, but described for explanation)\ntemplate \u003ctypename T\u003e\nconcept value_constructible = std::constructible_from\u003cyyjson::value, T\u003e;\ntemplate \u003ctypename T\u003e\nconcept array_constructible = std::ranges::input_range\u003cT\u003e \u0026\u0026 value_constructible\u003cstd::ranges::range_value_t\u003cT\u003e\u003e;\ntemplate \u003ctypename T\u003e\nconcept object_constructible = std::ranges::input_range\u003cT\u003e \u0026\u0026 key_value_like\u003cstd::ranges::range_value_t\u003cT\u003e\u003e \u0026\u0026\n                               value_constructible\u003cstd::tuple_element_t\u003c1, std::ranges::range_value_t\u003cT\u003e\u003e\u003e;\n\n// Conversion to primitive types\nyyjson::value(std::nullptr_t);\nyyjson::value(bool);\nyyjson::value(std::unsigned_integral);\nyyjson::value(std::signed_integral);\nyyjson::value(std::floating_point);\nyyjson::value(const std::string\u0026, [yyjson::copy_string_t]);\nyyjson::value(std::string_view, [yyjson::copy_string_t]);\nyyjson::value(const char*, [yyjson::copy_string_t]);\n\n// Conversion to array types\ntemplate \u003carray_constructible T\u003e\nyyjson::value(T\u0026\u0026, [yyjson::copy_string_t]);\n\n// Conversion to object types\ntemplate \u003cobject_constructible T\u003e\nyyjson::value(T\u0026\u0026, [yyjson::copy_string_t]);\n\n// std::initializer_list\nyyjson::value(std::initialize_list\u003cyyjson::value\u003e);\nyyjson::value(std::initialize_list\u003cyyjson::writer::key_value_pair\u003e, [yyjson::copy_string_t]);\n\n// Copy from other JSON classes\nusing reference_types = yyjson::{reader,writer}::[const_]{value,array,object}[_ref];\nyyjson::value(const reference_types\u0026);\nyyjson::value(reference_types\u0026);\nyyjson::value(reference_types\u0026\u0026);\n\n// Default constructor (null by default)\nyyjson::value();\n\n// Copy constructor\nyyjson::value(const yyjson::value\u0026) = default;\nyyjson::value(yyjson::value\u0026\u0026) = default;\n```\n\n**Member function**\n\n```cpp\n// Check the type of JSON\nbool is_null() const;       // null\nbool is_true() const;       // true\nbool is_false() const;      // false\nbool is_bool() const;       // bool\nbool is_uint() const;       // std::uint64_t\nbool is_sint() const;       // std::int64_t\nbool is_int() const;        // std::uint64_t/std::int64_t\nbool is_real() const;       // double\nbool is_num() const;        // std::uint64_t/std::int64_t/double\nbool is_string() const;     // string\nbool is_array() const;      // array\nbool is_object() const;     // object\nbool is_container() const;  // array/object\n\n// Get the content of the JSON value\nstd::optional\u003cstd::nullptr_t\u003e as_null() const;\nstd::optional\u003cbool\u003e as_bool() const;\nstd::optional\u003cstd::uint64_t\u003e as_uint() const;\nstd::optional\u003cstd::int64_t\u003e as_sint() const;\nstd::optional\u003cstd::int64_t\u003e as_int() const;\nstd::optional\u003cdouble\u003e as_real() const;\nstd::optional\u003cdouble\u003e as_num() const;\nstd::optional\u003cstd::string_view\u003e as_string() const;\nstd::optional\u003cyyjson::writer::const_array_ref\u003e as_array() const\u0026;\nstd::optional\u003cyyjson::writer::array_ref\u003e as_array() \u0026;\nstd::optional\u003cyyjson::writer::array\u003e as_array() \u0026\u0026;\nstd::optional\u003cyyjson::writer::const_object_ref\u003e as_object() const\u0026;\nstd::optional\u003cyyjson::writer::object_ref\u003e as_object() \u0026;\nstd::optional\u003cyyjson::writer::object\u003e as_object() \u0026\u0026;\n\n// operator=\nyyjson::value\u0026 yyjson::value::operator=(const yyjson::value\u0026);\nyyjson::value\u0026 yyjson::value::operator=(yyjson::value\u0026\u0026) noexcept;\ntemplate \u003cvalue_constructible T\u003e\nyyjson::value\u0026 yyjson::value::operator=(T\u0026\u0026);\ntemplate \u003cvalue_constructible T\u003e\nyyjson::value\u0026 yyjson::value::operator=(pair_like\u003cT, yyjson::copy_string_t\u003e);\n\n// Cast\ntemplate\u003ctypename T\u003e\nT cast\u003cT\u003e() const;\ntemplate\u003ctypename T\u003e\nexplicit operator T() const;\n\n// Output JSON string\nyyjson::json_string write(WriteFlag write_flag = WriteFlag::NoFlag) const;\ntemplate \u003cyyjson_allocator Allocator\u003e\nyyjson::json_string write(Allocator alc, WriteFlag write_flag = WriteFlag::NoFlag) const;\n```\n\nConcepts `value_constructible`, `array_constructible`, and `object_constructible` are **NOT** defined in the library but are described in the above for explanation hereafter.\n\nSince yyjson does not copy (but refer) strings to JSON documents by default, it is possible to explicitly specify that strings will be copied to JSON by giving the tag type value `yyjson::copy_string` as the second argument to the constructor. For safety, the implicit copy occurs when `std::string\u0026\u0026` is converted to the JSON string without specifying `yyjson::copy_string`.\n\nJSON arrays and objects are constructed recursively from ranges as long as the *range's value type* can be constructed to JSON value. The `yyjson::copy_string` is also passed recursively if it is given. The same manner applies to the other functions of mutable JSON classes.\n\n**Example**\n\nSee also [Overview](#json-writer).\n\n```cpp\nusing namespace yyjson;\nusing namespace std::literals;\n\n// Conversion constructors\nauto v_bool = value(true);\nauto v_null = value(nullptr);\nauto v_int = value(-1);\nauto v_real = value(3.14);\n\n// String types\nauto strlit = \"string literal\";\nauto stdstr = \"string example\"s;\nauto strviw = std::string_view(stdstr);\n\n// Explicit copy\nauto v_lit_cp = value(strlit, copy_string);\nauto v_str_cp = value(stdstr, copy_string);\nauto v_viw_cp = value(strviw, copy_string);\n\n// No copy: the life time of a string must be managed\nauto v_lit = value(strlit);\nauto v_str = value(stdstr);\nauto v_viw = value(strviw);\n\n// If the original string is modified, the uncopied JSON string seems to be also modified.\n// (but string length is not changed; it may occur memory access violation)\nstdstr = \"modified string\";\nstd::cout \u003c\u003c v_str_cp.write() \u003c\u003c std::endl;\n// \"string example\"\nstd::cout \u003c\u003c v_str.write() \u003c\u003c std::endl;\n// \"modified strin\"\n\n// Implicitly copy string if the argument type is `std::string\u0026\u0026` for safety\nauto v_str_cp = value(std::move(stdstr));\n```\n\n#### `yyjson::array`\n\nThe mutable JSON array class is created by the *range of JSON value constructible* or the `yyjson::value::as_array` member function. This class adapts `std::ranges::input_range` concept.\n\n**Constructor**\n\n```cpp\n// Conversion from range\ntemplate \u003carray_constructible T\u003e\nyyjson::array(array_constructible, [yyjson::copy_string]);\n\n// std::initializer_list\nyyjson::array(std::initialize_list\u003cvalue\u003e);\n\n// Copy from other JSON classes\n// throw yyjson::bad_cast if the JSON value is not convertible to a JSON array\nusing reference_types = yyjson::{reader,writer}::[const_]{value,array}[_ref];\nyyjson::array(const reference_types\u0026);\nyyjson::array(reference_types\u0026);\nyyjson::array(reference_types\u0026\u0026);\n\n// Default constructor (empty by default)\nyyjson::array();\n\n// Copy constructor\nyyjson::array(const yyjson::array\u0026) = default;\nyyjson::array(yyjson::array\u0026\u0026) = default;\n```\n\n**Member function**\n\n```cpp\n// STL-like functions\nconst_array_iter cbegin() const;\nconst_array_iter cend() const;\nconst_array_iter begin() const;\nconst_array_iter end() const;\narray_iter begin();\narray_iter end();\nyyjson::writer::const_value_ref front() const;\nyyjson::writer::value_ref front();\nyyjson::writer::const_value_ref back() const;\nyyjson::writer::value_ref back();\nyyjson::writer::const_value_ref operator[](std::size_t) const;  // Note: O(N)\nyyjson::writer::value_ref operator[](std::size_t);              // Note: O(N)\nstd::size_t size() const;\nbool empty() const;\nvoid erase(std::size_t);\nvoid clear();\n\n// Insert value_constructible\ntemplate \u003cvalue_constructible T\u003e\narray_iter emplace_back(T\u0026\u0026, [yyjson::copy_string_t]);\ntemplate \u003cvalue_constructible T\u003e\narray_iter emplace_front(T\u0026\u0026, [yyjson::copy_string_t]);\ntemplate \u003cvalue_constructible T\u003e\narray_iter emplace(std::size_t, T\u0026\u0026, [yyjson::copy_string_t]);  // Note: O(N)\n\n// Insert empty array/object\nyyjson::writer::array_ref  emplace_back(yyjson::empty_array_t);\nyyjson::writer::object_ref emplace_back(yyjson::empty_object_t);\nyyjson::writer::array_ref  emplace_front(yyjson::empty_array_t);\nyyjson::writer::object_ref emplace_front(yyjson::empty_object_t);\nyyjson::writer::array_ref  emplace(std::size_t, yyjson::empty_array_t);     // Note: O(N)\nyyjson::writer::object_ref emplace(std::size_t, yyjson::empty_object_t);    // Note: O(N)\n\n// operator=\nyyjson::array\u0026 operator=(const yyjson::array\u0026);\nyyjson::array\u0026 operator=(yyjson::array\u0026\u0026) noexcept;\ntemplate \u003carray_constructible T\u003e\nyyjson::array\u0026 operator=(T\u0026\u0026);\ntemplate \u003carray_constructible T\u003e\nyyjson::array\u0026 operator=(pair_like\u003cT, yyjson::copy_string_t\u003e);\n\n// Cast\ntemplate\u003ctypename T\u003e\nT cast\u003cT\u003e() const;\ntemplate\u003ctypename T\u003e\nexplicit operator T() const;\n\n// Output JSON string\nyyjson::json_string write(WriteFlag write_flag = WriteFlag::NoFlag) const;\ntemplate \u003cyyjson_allocator Allocator\u003e\nyyjson::json_string write(Allocator alc, WriteFlag write_flag = WriteFlag::NoFlag) const;\n\n// Range concept\nstd::ranges::range_value_t\u003cyyjson::array\u0026\u003e       -\u003e yyjson::writer::value_ref\nstd::ranges::range_value_t\u003cconst yyjson::array\u0026\u003e -\u003e yyjson::writer::const_value_ref\n```\n\nThe `yyjson::array` is designed to be implemented like STL containers but extended to allow easy insertion of an empty array and object with `yyjson::empty_array` and `yyjson::empty_object` tag type values, respectively. These special modifiers also have a different return type, which is not an array iterator but a reference to a newly inserted empty array/object of type `yyjson::writer::array_ref` or `yyjson::writer::object_ref`. These are introduced to optimize the performance by not creating a new JSON array/object; see also the [performance best practices](#performance-best-practices) section.\n\nIterator classes are not exposed. They are tentatively described as `array_iter` and `const_array_iter` in the above.\n\n**Example**\n\nSee also [Overview](#json-writer).\n\n```cpp\nusing namespace yyjson;\n\n// Create a new mutable JSON array from range\nauto arr = array(std::vector{1, 2, 3});\n\n// Insert a new value\narr.emplace_back(\"4\");\n\n// Insert an empty array and insert values into it\nauto nested = arr.emplace_back(empty_array);\nnested.emplace_back(5.0);\nnested.emplace_back(\"6\");\nnested.emplace_back(7);\n\nstd::cout \u003c\u003c arr.write() \u003c\u003c std::endl;\n// [1,2,3,\"4\",[5.0,\"6\",7]]\n\n// Range-based for loop\nfor (auto\u0026\u0026 v : arr)\n{\n    if (v.is_int()) v = *v.as_int() * 2;\n}\n\nstd::cout \u003c\u003c arr.write() \u003c\u003c std::endl;\n// [2,4,6,\"4\",[5.0,\"6\",7]]\n```\n\n#### `yyjson::object`\n\nThe mutable JSON object class is created by the *key-value range of JSON value constructible* or the `yyjson::value::as_object` member function. This class adapts `std::ranges::input_range` concept.\n\n**Constructor**\n\n```cpp\n// Conversion from key-value-like range\ntemplate \u003cobject_constructible T\u003e\nyyjson::object(object_constructible, [yyjson::copy_string]);\n\n// std::initializer_list\nyyjson::object(std::initialize_list\u003cyyjson::writer::key_value_pair\u003e);\n\n// Copy from other JSON classes\n// throw yyjson::bad_cast if the JSON value is not convertible to a JSON object\nusing reference_types = yyjson::{reader,writer}::[const_]{value,object}[_ref];\nyyjson::object(const reference_types\u0026);\nyyjson::object(reference_types\u0026);\nyyjson::object(reference_types\u0026\u0026);\n\n// Default constructor (empty by default)\nyyjson::object();\n\n// Copy constructor\nyyjson::object(const yyjson::object\u0026) = default;\nyyjson::object(yyjson::object\u0026\u0026) = default;\n```\n\n**Member function**\n\n```cpp\n// STL-like functions\nconst_object_iter cbegin() const;\nconst_object_iter cend() const;\nconst_object_iter begin() const;\nconst_object_iter end() const;\nconst_object_iter find(std::string_view key) const; // Note: O(N)\nobject_iter begin();\nobject_iter end();\nobject_iter find(std::string_view key); // Note: O(N)\nstd::size_t size() const;\nbool empty() const;\nvoid erase(std::string_view);\nvoid clear();\nbool contains(std::string_view key) const;  // Note: O(N)\n\n// Insert value_constructible, no duplicate check\ntemplate \u003cvalue_constructible ValueType\u003e\nobject_iter emplace(KeyType\u0026\u0026, ValueType\u0026\u0026, [yyjson::copy_string_t]);\n// Note: O(N), throw std::out_of_range if a key is not found\nyyjson::writer::const_value_ref operator[](std::string_view) const;\n// Note: O(N), construct default value if a key is not found\nyyjson::writer::value_ref operator[](std::string_view);\n\n// Insert empty array/object\nyyjson::writer::array_ref  emplace(KeyType\u0026\u0026, yyjson::empty_array_t,  [yyjson::copy_string_t]);\nyyjson::writer::object_ref emplace(KeyType\u0026\u0026, yyjson::empty_object_t, [yyjson::copy_string_t]);\n\n// Try emplace, O(N) for duplicate check\ntemplate \u003cvalue_constructible ValueType\u003e\nstd::pair\u003cobject_iter, bool\u003e try_emplace(KeyType\u0026\u0026, ValueType\u0026\u0026, [yyjson::copy_string_t]);\n\n// operator=\nyyjson::object\u0026 operator=(const yyjson::object\u0026);\nyyjson::object\u0026 operator=(yyjson::object\u0026\u0026) noexcept;\ntemplate \u003cobject_constructible T\u003e\nyyjson::object\u0026 operator=(T\u0026\u0026);\ntemplate \u003cobject_constructible T\u003e\nyyjson::object\u0026 operator=(pair_like\u003cT, yyjson::copy_string_t\u003e);\n\n// Cast\ntemplate\u003ctypename T\u003e\nT cast\u003cT\u003e() const;\ntemplate\u003ctypename T\u003e\nexplicit operator T() const;\n\n// Output JSON string\nyyjson::json_string write(WriteFlag write_flag = WriteFlag::NoFlag) const;\ntemplate \u003cyyjson_allocator Allocator\u003e\nyyjson::json_string write(Allocator alc, WriteFlag write_flag = WriteFlag::NoFlag) const;\n\n// Range concept\nusing yyjson::writer::key_value_ref_pair = std::pair\u003cstd::string_view, yyjson::reader::value_ref\u003e;\nusing yyjson::writer::const_key_value_ref_pair = std::pair\u003cstd::string_view, yyjson::reader::const_value_ref\u003e;\nstd::ranges::range_value_t\u003cyyjson::object\u0026\u003e       -\u003e yyjson::writer::key_value_ref_pair\nstd::ranges::range_value_t\u003cconst yyjson::object\u0026\u003e -\u003e yyjson::writer::const_key_value_ref_pair\n```\n\nThe `yyjson::object` is also designed to be implemented like STL map containers and to allow easy insertion of an empty array and object with `yyjson::empty_array` and `yyjson::empty_object` tag type values, respectively. These special modifiers also have a different return type, which is not an object iterator but a reference to a newly inserted empty array/object of type `yyjson::writer::array_ref` or `yyjson::writer::object_ref`. These are introduced to optimize the performance by not creating a new JSON array/object; see also the [performance best practices](#performance-best-practices) section.\n\nIterator classes are not exposed. They are tentatively described as `obejct_iter` and `const_object_iter` in the above.\n\n\u003e [!NOTE]\n\u003e The `emplace` member functions do **NOT** check for key duplication.\n\n**Example**\n\nSee also [Overview](#json-writer).\n\n```cpp\nusing namespace yyjson;\n\n// Create a new mutable JSON object from a key-value-like range\nauto obj = object(std::map\u003cstd::string, std::map\u003cstd::string, int\u003e\u003e{{\"key0\", {{\"a\", 0}, {\"b\", 1}}},\n                                                                    {\"key1\", {{\"c\", 2}, {\"d\", 3}}}});\n\n// Insert a new key-value pair\nobj.emplace(\"key2\", std::vector{4, 5});\n\n// Insert an empty object and insert key-value pairs into it\nauto nested = obj.emplace(\"key3\", empty_object);\nnested.emplace(\"g\", 6);\nnested.emplace(\"h\", 7);\n\nstd::cout \u003c\u003c obj.write() \u003c\u003c std::endl;\n// {\"key0\":{\"a\":0,\"b\":1},\"key1\":{\"c\":2,\"d\":3},\"key2\":[4,5],\"key3\":{\"g\":6,\"h\":7}}\n\n// Key access and modify\nobj[\"key2\"] = std::map\u003cstd::string, int\u003e{{\"e\", 4}, {\"f\", 5}};\n\nstd::cout \u003c\u003c obj.write() \u003c\u003c std::endl;\n// {\"key0\":{\"a\":0,\"b\":1},\"key1\":{\"c\":2,\"d\":3},\"key2\":{\"e\":4,\"f\":5},\"key3\":{\"e\":6,\"f\":7}}\n```\n\n### Allocator classes\n\nThis library provides the following memory allocator wrappers for reading and writing JSON strings. They are useful to improve performance for tasks that parse or stringify multiple JSON strings to avoid multiple memory allocations and deallocations.\n\n#### `yyjson::dynamic_allocator`\n\nThis is a smart pointer wrapper for the dynamic allocator of yyjson. The lifetime of the allocator's internal smart pointer will be tied to JSON objects and strings created by `read`/`write` (member) functions, respectively. This allocator is for general purposes since it can automatically expand the buffer.\n\n\u003e [!NOTE]\n\u003e This allocator is only available for yyjson \u003e= v0.8.0.\n\n**Constructor**\n\n```cpp\n// Default constructors\ndynamic_allocator() = default;\ndynamic_allocator(const dynamic_allocator\u0026) = default;\ndynamic_allocator(dynamic_allocator\u0026\u0026) noexcept = default;\n```\n\n**Member function**\n\n```cpp\n// Reset to a new dynamic allocator\nvoid reset() const;\n```\n\n**Example**\n\n```cpp\nusing namespace yyjson;\n\nauto alc = dynamic_allocator();\n\n// Read JSON string using dynamic allocator\nauto read_json(const string\u0026 json)\n{\n    return read(json, alc);\n}\n\n// Create JSON string using dynamic allocator\nauto write_json(const auto\u0026 json)\n{\n    return json.write(alc);\n}\n```\n\n#### `yyjson::pool_allocator`\n\nThis is a smart pointer wrapper for the fixed-size memory allocator of yyjson. The lifetime of the allocator's internal smart pointer will be tied to JSON objects and strings created by `read`/`write` (member) functions, respectively. This allocator is useful when the required memory size is known; for reading JSON strings. Since the buffer size is fixed, the `read` function using the single buffer would be faster than the dynamic allocator.\n\nIf the free space of the buffer is not large enough, the `read` or `write` (member) function will throw an exception. The `reset` function re-creates the buffer with the specified size and the `reserve` function can be used to expand the buffer size to be required.\n\n**Constructor**\n\n```cpp\n// Default constructors\npool_allocator() = default;\npool_allocator(const pool_allocator\u0026) = default;\npool_allocator(pool_allocator\u0026\u0026) noexcept = default;\n\n// Allocate specified bytes of buffer\nexplicit pool_allocator(std::size_t size_byte);\n// Allocate a buffer large enough to read the specified JSON string with read flags\nexplicit pool_allocator(std::string_view json, ReadFlag flag = ReadFlag::NoFlag);\n```\n\n**Member function**\n\n```cpp\n// Return the buffer size\nstd::size_t size() const;\n// Reset to a new fixed size allocator\nvoid reset(std::size_t size_byte = 0);\nvoid reset(std::string_view json, ReadFlag flag = ReadFlag::NoFlag);\n// Expand the buffer to required size\nvoid reserve(std::size_t size_byte);\nvoid reserve(std::string_view json, ReadFlag flag = ReadFlag::NoFlag);\n// Check if the buffer, when empty, is large enough to read the specified JSON string with read flags\nbool check_capacity(std::string_view json, ReadFlag = ReadFlag::NoFlag) const;\n```\n\n**Example**\n\n```cpp\nusing namespace yyjson;\n\n// Create a single buffer allocator\nauto alc = pool_allocator();\n\n// Read JSON string using pool allocator multiple times\nfor (const auto\u0026 json : json_strings)\n{\n    // Expand the buffer required to read JSON string\n    alc.reserve(json);\n    auto val = read(json, alc);\n    // ...\n}\n```\n\n##### `yyjson::stack_pool_allocator\u003cstd::size_t Byte\u003e`\n\nThis allocator is a fixed-size and stack-allocated buffer. The buffer size is specified by the template parameter and thus the buffer size cannot be changed. The required buffer size must be known in advance.\n\n\u003e [!NOTE]\n\u003e The lifetime of `stack_pool_allocator` is not managed automatically unlike other allocators. Not to destroy the allocator while an instance of the JSON object/string is in use.\n\n**Constructor**\n\n```cpp\n// Default constructors\nstack_pool_allocator() = default;\nstack_pool_allocator(const stack_pool_allocator\u0026) = default;\nstack_pool_allocator(stack_pool_allocator\u0026\u0026) noexcept = default;\n```\n\n**Member function**\n\n```cpp\n// Return the buffer size\nconstexpr std::size_t size() const;\n// Reset to a new fixed size allocator\nvoid reset();\n// Check if the buffer, when empty, is large enough to read the specified JSON string with read flags\nbool check_capacity(std::string_view json, ReadFlag = ReadFlag::NoFlag) const\n```\n\n### Serialize and deserialize JSON\n\nIn the cpp-yyjson, conversion from C++ objects to JSON values is very flexible and vice versa.\n\nConversion to JSON values, arrays, and objects is provided by their respective constructors. Conversely, to convert from a JSON value, call the `yyjson::cast` function or `static_cast`.\n\n```cpp\nusing namespace yyjson;\n\nauto val = value(3);\n// -\u003e 3\nauto num = cast\u003cint\u003e(val);\n// -\u003e 3\n\nauto arr = array(std::vector\u003cstd::vector\u003cint\u003e\u003e{{1, 2}, {3, 4}});\n// -\u003e [[1, 2], [3, 4]]\nauto vec = cast\u003cstd::vector\u003cstd::vector\u003cint\u003e\u003e\u003e(arr);\n// -\u003e {{1, 2}, {3, 4}}\n\nauto obj = object(std::unordered_map\u003cstd::string, double\u003e{{\"first\", 1.5}, {\"second\", 2.5}, {\"third\", 3.5}});\n// -\u003e {\"first\": 1.5, \"second\": 2.5, \"third\": 3.5}\nauto map = cast\u003cstd::unordered_map\u003cstd::string, double\u003e\u003e(obj);\n// -\u003e {{\"first\", 1.5}, {\"second\", 2.5}, {\"third\", 3.5}}\n```\n\nIf the conversion fails, the `yyjson::bad_cast` exception is thrown or a compile error occurs.\n\nNote that the `cast` function and `static_cast` may give a different result. The `cast` function tries to convert JSON directly to a given type, while `static_cast` follows the C++ conversion rules. For example, `cast\u003cstd::optional\u003cint\u003e\u003e(value(nullptr))` succeeds but `static_cast\u003cstd::optional\u003cint\u003e\u003e(value(nullptr))` does not. This is because `std::optional\u003cint\u003e` has a conversion constructor from `int`, but `value(nullptr)` cannot be converted to `int`.\n\nIn addition to the above, the following four methods are provided for converting *arbitrary* C++ types from/to JSON value, array and object:\n\n1.  Pre-defined STL casters.\n2.  Conversion using compile-time reflection.\n3.  Registration of field names with `VISITABLE_STRUCT` macro.\n4.  User-defined casters.\n\n#### Pre-defined STL casters\n\nSeveral casters from/to STL classes to JSON are pre-defined in the library for convenience. Currently, available STL types are as follows:\n\n*   `std::optional`\n*   `std::variant`\n*   `std::tuple`-like\n*   `std::vector\u003cbool\u003e` (specialized)\n\nFor example, if you receive elements of multiple types, casts from/to `std::optional` or `std::variant` are useful.\n\n```cpp\nusing namespace yyjson;\n\nauto val = value(std::optional\u003cint\u003e(3));    // serialize\n// -\u003e 3\nauto num = cast\u003cstd::optional\u003cint\u003e\u003e(val);   // deserialize\n// -\u003e 3\n\nauto val = value(std::optional\u003cint\u003e(std::nullopt)); // serialize\n// -\u003e null\nauto num = cast\u003cstd::optional\u003cint\u003e\u003e(val);           // deserialize\n// -\u003e std::nullopt\n\nauto val = value(std::variant\u003cstd::monostate, int, std::string\u003e());     // serialize\n// -\u003e null\nauto var = cast\u003cstd::variant\u003cstd::monostate, int, std::string\u003e\u003e(val);   // deserialize\n// -\u003e std::monostate()\n\nauto val = value(std::variant\u003cstd::monostate, int, std::string\u003e(1));    // serialize\n// -\u003e 1\nauto var = cast\u003cstd::variant\u003cstd::monostate, int, std::string\u003e\u003e(val);   // deserialize\n// -\u003e 1\n\nauto val = value(std::variant\u003cstd::monostate, int, std::string\u003e(\"a\"));  // serialize\n// -\u003e \"a\"\nauto var = cast\u003cstd::variant\u003cstd::monostate, int, std::string\u003e\u003e(val);   // deserialize\n// -\u003e \"a\"\n```\n\nFor a multi-type JSON array or object, using the caster for `std::tuple` is valuable and efficient.\n\n```cpp\nusing namespace yyjson;\n\nauto tpl_arr = std::tuple{nullptr, true, \"2\", 3.0, std::tuple{4.0, \"5\", false}};\nauto json_arr = array(tpl_arr);                             // serialize\n// -\u003e [null, true, \"2\", 3.0, [4.0, \"5\", false]]\nauto tpl_arr2 = cast\u003cdecltype(tpl_arr)\u003e(arr);               // deserialize\n// -\u003e {nullptr, true, \"2\", 3.0, {4.0, \"5\", false}}\n\nauto tpl_obj = std::tuple{std::pair{\"number\", 1.5}, std::pair{\"error\", nullptr}, std::pair{\"text\", \"abc\"}};\nauto json_obj = object(tpl_obj);                            // serialize\n// -\u003e {\"number\": 1.5, \"error\": null, \"text\": \"abc\"}\nauto tpl_obj2 = cast\u003cdecltype(tpl_obj)\u003e(json_obj);          // deserialize\n// -\u003e {{\"number\", 1.5}, {\"error\", nullptr}, {\"text\", \"abc\"}}\n```\n\n#### Automatic casting with compile-time reflection\n\nThe compile-time reflection is supported to automatically convert C++ struct/class to JSON object and vice versa. This feature is provided by using [field-reflection](https://https://github.com/yosh-matsuda/field-reflection) and is included in this library.\n\nIf a C++ struct/class satisfies the `field_reflection::field_namable` concept (see [field-reflection](https://https://github.com/yosh-matsuda/field-reflection)), it is possible to automatically convert from/to JSON objects with its field names and no need to write a caster definition or registration of field names with macros explained later.\n\nIn practice, the following conditions are required for C++ types for automatic conversion:\n\n*   default initializable\n*   [aggregate type](https://en.cppreference.com/w/cpp/types/is_aggregate)\n*   not a derived class\n*   no reference member\n\nExample:\n\n```cpp\nusing namespace yyjson;\n\nstruct X\n{\n    int a;\n    std::optional\u003cdouble\u003e b;\n    std::string c = \"default\";\n};\n\n// serialize struxt X to JSON object with field-name reflection\nauto reflectable = X{.a = 1, .b = std::nullopt, .c = \"x\"};\nauto serialized = object(visitable);\n// -\u003e {\"a\":1,\"b\":null,\"c\":\"x\"}\n\n// deserialize JSON object into struct X with field-name reflection\nauto deserialized = cast\u003cX\u003e(serialized);\n// -\u003e X{.a = 1, .b = std::nullopt, .c = \"x\"}\n```\n\n#### Registration of field names of struct/class\n\nEven if the compile-time reflection is *NOT* available for a C++ type or you want to partially select the fields to be converted, it is possible to register the field names of the C++ type with the `VISITABLE_STRUCT` macro:\n\n```cpp\n// register fields except `c` on purpose\nVISITABLE_STRUCT(X, a, b);\n\n// serialize visitable struxt X to JSON object\nauto visitable = X{.a = 1, .b = std::nullopt, .c = \"x\"};\nauto serialized = object(visitable);\n// -\u003e {\"a\":1,\"b\":null}\n\n// deserialize JSON object into struct X\nauto deserialized = cast\u003cX\u003e(serialized);\n// -\u003e X{.a = 1, .b = std::nullopt, .c = \"default\"}\n```\n\n#### User-defined casters\n\nThe most flexible way to convert C++ types to/from JSON is to define custom casters. The custom conversion always has the highest priority among the other conversions. To define a custom caster, specialize the `yyjson::caster` struct template and implement the `from_json` and/or `to_json` template functions.\n\nThe example implementation of the `from_json` template function is as follows:\n\n```cpp\ntemplate \u003c\u003e\nstruct yyjson::caster\u003cX\u003e\n{\n    template \u003ctypename Json\u003e\n    static X from_json(const Json\u0026 json)\n    {\n        if (const auto obj = json.as_object(); obj.has_value())\n        {\n            auto result = X();\n            for (const auto\u0026 kv : *obj)\n            {\n                if (kv.first == \"a\")\n                    result.a = yyjson::cast\u003cdecltype(result.a)\u003e(kv.second);\n                else if (kv.first == \"b\")\n                    result.b = yyjson::cast\u003cdecltype(result.b)\u003e(kv.second);\n                else if (kv.first == \"c\")\n                    result.c = yyjson::cast\u003cdecltype(result.c)\u003e(kv.second);\n            }\n            return result;\n        }\n        throw bad_cast(fmt::format(\"{} is not constructible from JSON\", NAMEOF_TYPE(X)));\n    }\n};\n```\n\nThe `from_json` template function has a JSON value as an argument template and must return type `X`.\n\nFor the `to_json` function, there are two ways. The first way is to define a *translator* for a *value_constructible* type and return it. The return type must be `value_constructible`:\n\n```cpp\ntemplate \u003c\u003e\nstruct yyjson::caster\u003cX\u003e\n{\n    template \u003ctypename... Ts\u003e\n    static auto to_json(const X\u0026 x, Ts...)\n    {\n        // Convert X object to JSON string\n        return fmt::format(\"{} {} {}\", x.a, (x.b ? fmt::format(\"{}\", *x.b) : \"null\"), x.c);\n    }\n};\n```\n\nThe `to_json` function takes a `copy_string` in the last argument, but this can be ignored if not needed.\n\nThe second way is to create a JSON value/array/object directly in the `to_json` function. The example for the class `X` equivalent to automatic casting or `VISITABLE_STRUCT` registration is as follows.\n\n```cpp\ntemplate \u003c\u003e\nstruct yyjson::caster\u003cX\u003e\n{\n    template \u003ctypename... Ts\u003e\n    static void to_json(writer::object_ref\u0026 obj, const X\u0026 x, Ts... ts)\n    {\n        obj.emplace(\"a\", x.a, ts...);\n        obj.emplace(\"b\", x.b, ts...);\n        obj.emplace(\"c\", x.c, ts...);\n    }\n};\n```\n\nThe first argument type is a target JSON class to create. There are 3 options, `writer::object_ref\u0026`, `writer::array_ref\u0026` and `writer::value_ref\u0026`; if you want to convert the class `X` to JSON array, the first argument should be `writer::array_ref\u0026`.\n\nThe casters are applied recursively to convert from/to JSON classes including custom casters. It is not always necessary to implement both `from_json` and `to_json` functions, and the two conversions do not have to be symmetric.\n\n### Performance best practices\n\nCreating a new `yyjson::value`, `yyjson::array`, or `yyjson::object` is expensive. This is one point we should be aware of when building JSON to maximize performance.\n\n#### (1) JSON array/object construction\n\nAlthough JSON arrays and objects can be constructed from `std::initializer_list\u003cvalue\u003e`, which is useful and intuitive, using `std::tuple` and `std::pair` is more efficient as it avoids the construction of `yyjson::value`. The drawback is that you have to write the type in every bracket.\n\n```cpp\nusing namespace yyjson;\n\n// 🙁 Construction from std::initializer_list is costly\nobject json = {{\"id\", 1},\n               {\"pi\", 3.141592},\n               {\"name\", \"example\"},\n               {\"array\", {0, 1, 2, 3, 4}},\n               {\"currency\", {{\"USD\", 129.66}, {\"EUR\", 140.35}, {\"GBP\", 158.72}}},\n               {\"success\", true}};\n\n// 🙄 Construction from std::tuple is efficient, but it seems tedious to have to write the type every time.\nobject json = std::tuple{std::pair{\"id\", 1},\n                         std::pair{\"pi\", 3.141592},\n                         std::pair{\"name\", \"example\"},\n                         std::pair{\"array\", std::tuple{0, 1, 2, 3, 4}},\n                         std::pair{\"currency\", std::tuple{std::pair{\"USD\", 129.66}, std::pair{\"EUR\", 140.35},\n                                                          std::pair{\"GBP\", 158.72}}},\n                         std::pair{\"success\", true}};\n```\n\n#### (2) JSON array/object insertion\n\nWhen creating a nested JSON array or object, it is more efficient to insert an empty array/object rather than to construct a new array/object.\n\n```cpp\nusing namespace yyjson;\n\nauto obj = object();\n\n// 🙁 Create a new object and insert it\nauto nested = object();\nobj.emplace(\"currency\", nested);\n\n// 😀 Insert an empty object and use returned object reference\nauto nested = obj.emplace(\"currency\", empty_object);\n\nnested.emplace(\"USD\", 129.66);\nnested.emplace(\"EUR\", 140.35);\nnested.emplace(\"GBP\", 158.72);\n```\n\n#### (3) Multi-threaded JSON construction\n\nOn the other hand, creating a new array/object may be useful for the multi-threaded construction of large JSON. The following example creates a 1000x1000000 JSON array with 4 threads using [`BS::thread_pool`](https://github.com/bshoshany/thread-pool). In this example, a speedup of about 3x compared to single threading was measured.\n\n```cpp\n#include \"BS_thread_pool.hpp\"\n\nusing namespace yyjson;\n\nauto nums = std::vector\u003cdouble\u003e(1000000);\nstd::iota(nums.begin(), nums.end(), 0);\n\n// Multi-threaded construction of 1000x1000000 JSON array\nauto tp = BS::thread_pool(4);\nauto parallel_results = tp.parallelize_loop(0, 1000,\n                                            [\u0026nums](const int a, const int b)\n                                            {\n                                                auto result = std::vector\u003carray\u003e();\n                                                result.reserve(b - a);\n                                                for (int i = a; i \u003c b; ++i) result.emplace_back(nums);\n                                                return result;\n                                            }).get();\nauto arr = yyjson::array();\nfor (auto\u0026\u0026 vec : parallel_results)\n    for (auto\u0026\u0026 a : vec) arr.emplace_back(std::move(a));\n\n// Single-threaded version equivalent to the above\nauto arr = yyjson::array();\nfor (auto i = 0; i \u003c 1000; ++i) arr.emplace_back(nums);\n```\n\n### Misc\n\n#### Support for {fmt} format\n\nThe cpp-yyjson defines a specialization of `fmt::formatter` of the [{fmt}](https://github.com/fmtlib/fmt) library. The JSON classes are formattable as follows:\n\n```cpp\nusing namespace yyjson;\n\nauto obj = object(...);\nauto json_str = fmt::format(\"{}\", obj);\nfmt::print(\"{}\", obj);\n```\n\n#### Object dependence safety\n\nUnlike the original yyjson, cpp-yyjson can safely add the same JSON value/array/object to the container multiple times.\n\n```cpp\nusing namespace yyjson;\n\nauto src_vec = std::vector{value(\"0\"), value(1), value(2.0)};\nauto arr = array();\nfor (auto\u0026\u0026 v : src_vec)\n{\n    // the first addition\n    arr.emplace_back(v);\n}\nfor (auto\u0026\u0026 v : src_vec)\n{\n    // the second addition: makes a copy and append it\n    arr.emplace_back(v);\n}\n```\n\n#### Conversion from immutable to mutable\n\nIf you want to make an immutable JSON instance writable, convert it to a mutable type.\n\n```cpp\nusing namespace yyjson;\n\nstd::string_view json_str = R\"(\n{\n    \"id\": 1,\n    \"pi\": 3.141592,\n    \"name\": \"🫠\",\n    \"array\": [0, 1, 2, 3, 4],\n    \"currency\": {\n        \"USD\": 129.66,\n        \"EUR\": 140.35,\n        \"GBP\": 158.72\n    },\n    \"success\": true\n})\";\n\n// Read JSON string and make mutable\nauto obj = object(read(json_str));\nobj[\"name\"] = \"❤️\";\n```\n\n## Author\n\nYoshiki Matsuda ([@yosh-matsuda](https://github.com/yosh-matsuda))\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyosh-matsuda%2Fcpp-yyjson","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyosh-matsuda%2Fcpp-yyjson","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyosh-matsuda%2Fcpp-yyjson/lists"}