{"id":18720462,"url":"https://github.com/stiffstream/json_dto","last_synced_at":"2025-04-11T01:03:03.206Z","repository":{"id":48534192,"uuid":"131745915","full_name":"Stiffstream/json_dto","owner":"Stiffstream","description":"A small header-only library for converting data between json representation and c++ structs","archived":false,"fork":false,"pushed_at":"2024-07-22T08:40:58.000Z","size":373,"stargazers_count":150,"open_issues_count":4,"forks_count":18,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-04-11T01:02:53.570Z","etag":null,"topics":["cplusplus","cpp","cpp14","dto","json","linux","rapidjson","windows"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Stiffstream.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}},"created_at":"2018-05-01T18:04:53.000Z","updated_at":"2025-03-10T02:46:18.000Z","dependencies_parsed_at":"2024-06-12T19:26:15.782Z","dependency_job_id":"c474765f-9c3e-45e1-a1c2-8089c5abbd7b","html_url":"https://github.com/Stiffstream/json_dto","commit_stats":null,"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Stiffstream%2Fjson_dto","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Stiffstream%2Fjson_dto/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Stiffstream%2Fjson_dto/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Stiffstream%2Fjson_dto/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Stiffstream","download_url":"https://codeload.github.com/Stiffstream/json_dto/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248322597,"owners_count":21084336,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["cplusplus","cpp","cpp14","dto","json","linux","rapidjson","windows"],"created_at":"2024-11-07T13:31:10.070Z","updated_at":"2025-04-11T01:03:03.182Z","avatar_url":"https://github.com/Stiffstream.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"Table of Contents\n=================\n\n   * [Table of Contents](#table-of-contents)\n   * [What Is json_dto?](#what-is-json_dto)\n   * [What's new?](#whats-new)\n      * [v.0.3.4](#v034)\n      * [v.0.3.3](#v033)\n      * [v.0.3.2](#v032)\n      * [v.0.3.1](#v031)\n      * [v.0.3.0](#v030)\n      * [v.0.2.14](#v0214)\n      * [v.0.2.13](#v0213)\n      * [v.0.2.12](#v0212)\n      * [v.0.2.11](#v0211)\n      * [v.0.2.10](#v0210)\n      * [v.0.2.9](#v029)\n      * [v.0.2.8](#v028)\n      * [v.0.2.7](#v027)\n      * [v.0.2.6.2](#v0262)\n      * [v.0.2.6.1](#v0261)\n      * [v.0.2.6](#v026)\n      * [v.0.2.5](#v025)\n      * [v.0.2.4](#v024)\n      * [v.0.2.3](#v023)\n      * [v.0.2.2](#v022)\n      * [v.0.2.1](#v021)\n      * [v.0.2.0](#v020)\n   * [Obtain and build](#obtain-and-build)\n      * [Prerequisites](#prerequisites)\n      * [Obtaining](#obtaining)\n         * [Cloning of Git Repository](#cloning-of-git-repository)\n         * [MxxRu::externals recipe](#mxxruexternals-recipe)\n      * [Build](#build)\n   * [How to use it?](#how-to-use-it)\n      * [Getting started](#getting-started)\n      * [Non intrusive json_io()](#non-intrusive-json_io)\n      * [Supported field types](#supported-field-types)\n      * [Mandatory and optional fields](#mandatory-and-optional-fields)\n         * [Mandatory fields](#mandatory-fields)\n         * [Optional fields](#optional-fields)\n            * [Optional fields and std::optional](#optional-fields-and-stdoptional)\n      * [Array support](#array-support)\n         * [Array fields](#array-fields)\n         * [Arrays and to_json and from_json](#arrays-and-to_json-and-from_json)\n      * [Other types of containers](#other-types-of-containers)\n         * [Multimaps and multisets](#multimaps-and-multisets)\n      * [Nullable fields](#nullable-fields)\n      * [Complex types](#complex-types)\n      * [Inheritance](#inheritance)\n      * [Validators](#validators)\n         * [Standard validators](#standard-validators)\n      * [Representing several fields inside an array](#representing-several-fields-inside-an-array)\n      * [User defined IO](#user-defined-io)\n         * [Overloading of read_json_value and write_json_value](#overloading-of-read_json_value-and-write_json_value)\n         * [Usage of Reader_Writer](#usage-of-reader_writer)\n            * [Custom Reader_Writer with containers and nullable_t, and std::optional](#custom-reader_writer-with-containers-and-nullable_t-and-stdoptional)\n         * [Overloading of read_json_value/write_json_value for const_map_key_t/mutable_map_key_t](#overloading-of-read_json_valuewrite_json_value-for-const_map_key_tmutable_map_key_t)\n         * [Custom Reader_Writer and mutable/const_map_key_t](#custom-reader_writer-and-mutableconst_map_key_t)\n   * [License](#license)\n\nCreated by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)\n\n# What Is json_dto?\n\n*json_dto* library is a small header-only helper\nfor converting data between json representation\nand c++ structs. DTO here stands for data transfer object.\nIt was made and used as a part of a larger project\ninside [StiffStream](http://stiffstream.com).\nAnd since Fall 2016 is ready for public. We are still using it for\nworking with JSON in various projects.\n\n# What's new?\n\n## v.0.3.4\n\nSeveral new `to_json`, `from_json`, `to_stream` and `from_stream` functions\nthat accept Reader-Writer parameter. For example:\n\n```cpp\n// Type to be serialized.\nclass some_data {\npublic:\n\t...\n\ttemplate\u003ctypename Io\u003e void json_io(Io \u0026 io) {...}\n};\n\n// Special Reader-Writer for packing/unpacking instances of some_data.\nstruct some_data_reader_writer {\n\tvoid read( some_data \u0026 obj, const rapidjson::Value \u0026 from ) const {...}\n\n\tvoid write( const some_data \u0026 obj, rapidjson::Value \u0026 to, rapidjson::MemoryPoolAllocator\u003c\u003e \u0026 allocator ) const {...}\n};\n...\n// Object to be serialized.\nsome_data data_to_pack{...};\n// Serialization by using custom Reader-Writer object.\nauto json_string = json_dto::to_json(some_data_reader_writer{}, data_to_pack);\n```\n\nSee a new [deserialization example](./dev/sample/tutorial16.1/main.cpp) that shows how\na new `from_json` function can be used.\n\nThere is also [a new example](./dev/sample/tutorial18.1/main.cpp) that shows how a custom Reader-Writer may change representation of an item.\n\n## v.0.3.3\n\nSupport for storing of several fields into an array added.\n\n## v.0.3.2\n\nIt's a bug-fix release.\n\n## v.0.3.1\n\nSupport for `std::int8_t` and `std::uint8_t` added to json-dto.\n\n## v.0.3.0\n\nVersion 0.3.0 introduces a couple of breaking changes that can affect some users.\n\nPrevious versions of json-dto called `set_attr_null_value` function when 'null'\nvalue was found during deserialization. There were two overloads for\n`set_attr_null_value`: one for `nullable_t\u003cT\u003e` and another for all other cases.\nThe overload for `nullable_t\u003cT\u003e` reset the nullable field. The overload for all\nother cases threw an exception.\n\nIt's important to note that the manopt_policy trait wasn't used for handling\n'null' values.\n\nSince v.0.3.0 an updated approach of dealing with 'null' values is used. Now\nthe manopt_policy is used when 'null' is found during deserialization:\n`on_null` method from manopt_policy is now called when 'null' is found.\n\nFunction templates `set_attr_null_value` were removed. They were replaced by\nnew function templates `default_on_null` that have the same logic (but under\nthe new names).\n\nThis change means that if you have your own implementation of manopt_policy\nthen you have to add the `on_null` template method to it. For example:\n\n```cpp\nstruct my_manopt_policy\n{\n\ttemplate\u003ctypename Field_Type\u003e\n\tvoid on_field_not_defined(Field_Type \u0026) const { ... }\n\n\ttemplate\u003ctypename Field_Type\u003e\n\tbool\n\tis_default_value(Field_Type \u0026) const { ... }\n\n\ttemplate\u003ctypename Field_Type\u003e\n\tvoid on_null(Field_Type \u0026 f) const\n\t{\n\t\tjson_dto::default_on_null(f);\n\t}\n}\n```\n\nAlso it means that if you have your own specialization for\n`binder_read_from_implementation_t` class template then you have to replace a\ncall to `set_attr_null_value` by a call to manopt_policy's `on_null` method\n(see the description of `binder_read_from_implementation_t` for more info).\n\nA new binder function `mandatory_with_null_as_default` introduced. It allows\nbinding a mandatory attribute that allows 'null' value in JSON. If 'null' is\nfound during deserialization then a field of type `T` will receive `T{}` as a\nvalue (it means that `T` has to be DefaultConstructible). For example:\n\n```cpp\nstruct my_data\n{\n\tint type_{-1};\n\tstd::vector\u003cstd::string\u003e headers_;\n\t...\n\n\ttemplate\u003ctypename Json_Io\u003e\n\tvoid json_io(Json_Io \u0026 io)\n\t{\n\t\tio\n\t\t\t// In case of 'null' type_ will receive 0 (because int{} produces 0).\n\t\t\t\u0026 json_dto::mandatory_with_null_as_default( \"type\", type_ )\n\t\t\t// In case of 'null' headers_ will be empty\n\t\t\t// (because std::vector\u003cstd::string\u003e{} produces empty vector).\n\t\t\t\u0026 json_dto::mandatory_with_null_as_default( \"headers\", headers_ )\n\t\t\t...\n\t\t\t;\n\t}\n};\n```\n\n## v.0.2.14\n\nA new overload `to_strean` added that receives an instance of `pretty_writer_params_t` with parameters for RapidJSON's PrettyWriter:\n\n```cpp\nstd::ofstream target_file{...};\nmy_data my_obj;\n...\n// Make default serialization, without pretty-writer:\njson_dto::to_strean(target_file, my_obj);\n\n// Make serialization with pretty-writer:\njson_dto::to_stream(target_file, my_obj,\n\t\tjson_dto::pretty_writer_params_t{}\n\t\t\t\t.indent_char(' ')\n\t\t\t\t.indent_char_count(3u)\n\t\t\t\t.format_options(rapidjson::kFormatSingleLineArray));\n```\n\n## v.0.2.13\n\nA new overload `to_json` added that receives an instance of `pretty_writer_params_t` with parameters for RapidJSON's PrettyWriter:\n\n```cpp\nmy_data my_obj;\n...\n// Make default serialization, without pretty-writer:\nstd::string my_obj_image = json_dto::to_json(my_obj);\n\n// Make serialization with pretty-writer:\nstd::string my_obj_pretty_image = json_dto::to_json(my_obj,\n\t\tjson_dto::pretty_writer_params_t{}\n\t\t\t\t.indent_char(' ')\n\t\t\t\t.indent_char_count(3u)\n\t\t\t\t.format_options(rapidjson::kFormatSingleLineArray));\n```\n\n## v.0.2.12\n\nFunctions `mandatory`, `optional`, `optional_no_default`, and `optional_null` now accepts const- and rvalue references. That can be useful for types that have to be only serializable. For example:\n\n```cpp\nstruct demo {\n\tconst int priority_;\n\n\tstd::vector\u003cint\u003e version() const { return { 1, 2, 3 }; }\n\n\tconst std::string payload_;\n\n\tdemo(int priority, std::string payload)\n\t\t:\tpriority_{priority}, payload_{std::move(payload)}\n\t{}\n\n\ttemplate\u003ctypename Json_Io\u003e\n\tvoid json_io(Json_Io \u0026 io) {\n\t\tio \u0026 json_dto::mandatory(\"priority\", priority_)\n\t\t\t\u0026 json_dto::mandatory(\"version\", version())\n\t\t\t\u0026 json_dto::mandatory(\"payload\", payload_)\n\t\t\t;\n\t}\n};\n```\n\nPlease note that this code will lead to a compilation error in an attempt to deserialize an instance of `demo` type.\n\nThe class template `json_dto::binder_t` was refactored and now it uses several new customization points in the implementation: `binder_data_holder_t`, `binder_read_from_implementation_t` and `binder_write_to_implementation_t`. Those customization points allow to add a new functionality without modifying the json-dto source code.\n\nFor example, a user now can do something like:\n\n```cpp\nnamespace tricky_stuff {\n\ntemplate\u003ctypename F\u003e struct serialize_only_proxy {...};\n\ntemplate\u003ctypename F\u003e serialize_only_proxy\u003cF\u003e serialize_only(const F \u0026 f) {...}\n\ntemplate\u003ctypename F\u003e struct deserialize_only_proxy {...};\n\ntemplate\u003ctypename F\u003e deserialize_only_proxy\u003cF\u003e deserialize_only(F \u0026 f) {...}\n\n} // namespace tricky_stuff\n\nnamespace json_dto {\n\n... // Several partial specializations of binder_data_holder_t,\n    // binder_read_from_implementation_t and binder_write_to_implementation_t\n    // for tricky_stuff::serialize_only_proxy and tricky_stuff::deserialize_only_proxy.\n\n} // namespace json_dto\n\n\nstruct demo {\n\tconst int priority_;\n\n\tstd::vector\u003cint\u003e version() const { return { 1, 2, 3 }; }\n\n\tconst std::string payload_;\n\n\tstd::vector\u003cstd::string\u003e obsolete_properties_;\n\n\tdemo(int priority, std::string payload)\n\t\t:\tpriority_{priority}, payload_{std::move(payload)}\n\t{}\n\n\ttemplate\u003ctypename Json_Io\u003e\n\tvoid json_io(Json_Io \u0026 io) {\n\t\tio \u0026 json_dto::mandatory(\"priority\",\n\t\t\t\t\ttricky_stuff::serialize_only(priority_))\n\t\t\t\u0026 json_dto::mandatory(\"version\",\n\t\t\t\t\ttricky_stuff::serialize_only(version()))\n\t\t\t\u0026 json_dto::mandatory(\"payload\", payload_)\n\t\t\t\u0026 json_dto::optional_no_default(\"properties\",\n\t\t\t\t\ttricky_stuff::deserialize_only(obsolete_properties_)\n\t\t\t;\n\t}\n};\n```\n\nSeveral examples of how stuff like that can be implemented are shown in json_dto's `samples` folder: [serialize_only implementation](./dev/sample/tutorial20.1/main.cpp), [deserialize_only implementation](./dev/sample/tutorial20.2/main.cpp), [ignore_after_deserialization implementation](./dev/sample/tutorial20.3/main.cpp).\n\nSuch functions like `serialize_only` and `deserialize_only` can be useful in data-transformation code. For example, when we have to read some old data in JSON format, modify the data read and write it in a slightly different JSON.\n\n## v.0.2.11\n\nNew types `mutable_map_key_t\u003cT\u003e` and `const_map_key_t\u003cT\u003e` are now used for (de)serializing keys of map-like structures. See [the description below](#overloading-of-read_json_valuewrite_json_value-for-const_map_key_tmutable_map_key_t).\n\nA new Reader_Writer proxy `apply_to_content_t` added to address an issue of using custom Reader_Writers to the content of containers, `nullable_t` and `std::optional`. See [the description below](#custom-reader_writer-with-containers-and-nullable_t-and-stdoptional).\n\n## v.0.2.10\n\nAnother way of custom read/write operations added. It's based on specifying an instance of some user-supplied Reader_Writer type in the description of a field. See [Usage of Reader_Writer](#usage-of-reader_writer) for more details.\n\nFor support of that feature new overloads of `json_dto::mandatory`, `json_dto::optional`, and `json_dto::optional_no_default` have been added.\n\nThere is also a new `json_dto::write_json_value` overload:\n\n```cpp\nvoid write_json_value(\n\tconst rapidjson::Value::StringRefType \u0026 s,\n\trapidjson::Value \u0026 object,\n\trapidjson::MemoryPoolAllocator\u003c\u003e \u0026 allocator );\n```\n\nPlease note that `json_dto::string_ref_t` is just an alias for\n`rapidjson::Value::StringRefType`.\n\n## v.0.2.9\n\nNew overloads for ``from_json`` function:\n\n```cpp\n// Parses null-terminated string and returns a new object.\ntemplate\u003ctypename Type, unsigned Rapidjson_Parseflags = rapidjson::kParseDefaultFlags\u003e\nType from_json( const char * json );\n\n// Parses null-terminated string into alredy existed object.\ntemplate\u003ctypename Type, unsigned Rapidjson_Parseflags = rapidjson::kParseDefaultFlags\u003e\nvoid from_json( const char * json, Type \u0026 o );\n\n// Parses a string-view and returns a new object.\n// NOTE. string_ref_t is just an alias for RapidJSON's StringRefType.\ntemplate\u003ctypename Type, unsigned Rapidjson_Parseflags = rapidjson::kParseDefaultFlags\u003e\nType from_json( const string_ref_t \u0026 json );\n\n// Parses a string-view into alredy existed object.\ntemplate\u003ctypename Type, unsigned Rapidjson_Parseflags = rapidjson::kParseDefaultFlags\u003e\nvoid from_json( const string_ref_t \u0026 json, Type \u0026 o );\n```\n\nVersions with ``string_ref_t`` arguments are intended to be used in cases where a part of existing buffer should be parsed. For example:\n\n```cpp\nstd::vector\u003cchar\u003e pdu = extract_data();\n\n// string_view from C++17 is used just for a demonstration.\nstring_view headers;\nstring_view payload;\nstd::tie(headers, payload) = split_pdu_to_headers_and_payload(\u0026pdu.front(), pdu.size());\n\nauto parsed_payload = json_dto::from_json\u003cPayloadType\u003e(\n\t\tjson_dto::make_string_ref(payload.data(), payload.size());\n```\n\nVersions with ``const char *`` are added to resolve ambious overloads with\n``from_string(const std::string \u0026)`` and ``from_string(const string_ref_t \u0026)``\nin the following cases:\n\n```cpp\nauto payload = json_dto::from_json\u003cPayloadType\u003e(R\"JSON({\"id\":10})JSON\");\n```\n\nPlease note that ``string_ref_t`` is not ``std::string_view``. ``string_ref_t``\nis just an alias for RapidJSON's ``StringRefType``. And type ``StringRefType``\nhas a constructor is the form:\n\n```cpp\nStringRefType(const char * ch, SizeType length);\n```\n\nBut RapidJSON's ``SizeType`` is not ``std::size_t``. So if someone writes:\n\n```cpp\nstd::vector\u003cchar\u003e payload{...};\nauto data = json_dto::from_json\u003cPayloadType\u003e(\n\t\tjson_dto::string_ref_t{\u0026payload.front(), payload.size()} );\n```\n\nthere could be a warning from the compiler about narrowing of ``std::size_t``\nto ``SizeType``.\n\nA set on ``make_string_ref`` functions is added to json_dto to avoid such warnings:\n\n```cpp\nstring_ref_t make_string_ref(const char * v);\nstring_ref_t make_string_ref(const char * v, std::size_t length);\nstring_ref_t make_string_ref(const std::string \u0026 v);\n```\n\nUse those functions to avoid warnings from the compiler:\n\n```cpp\nstd::vector\u003cchar\u003e payload{...};\nauto data = json_dto::from_json\u003cPayloadType\u003e(\n\t\tjson_dto::make_string_ref(\u0026payload.front(), payload.size()) );\n```\n\n## v.0.2.8\n\nSupport for STL containers like `std::deque`, `std::list`, `std::forward_list`,\n`std::set`, `std::unordered_set`, `std::map` and `std::unordered_map` is implemented.\nThese types can be used as types of fields in a serialized type, for example:\n\n```cpp\n#include \u003cjson_dto/pub.hpp\u003e\n\n#include \u003cdeque\u003e\n#include \u003cset\u003e\n#include \u003cmap\u003e\n\nstruct my_message {\n\tstd::deque\u003cint\u003e ids_;\n\tstd::set\u003cstd::string\u003e tags_;\n\tstd::map\u003cstd::string, some_another_type\u003e props_;\n\t...\n\ttemplate\u003ctypename Json_Io\u003e\n\tvoid json_io(Json_Io \u0026 io) {\n\t\tio \u0026 json_dto::mandatory(\"ids\", ids_)\n\t\t\t\u0026 json_dto::mandatory(\"tags\", tags_)\n\t\t\t\u0026 json_dto::mandatory(\"properties\", props_)\n\t\t\t...\n\t\t\t;\n\t}\n};\n```\n\nThese types can also be used with `json_dto::from_json()` and `json_dto::to_json()` functions:\n\n```cpp\nauto messages = json_dto::from_json\u003c std::forward_list\u003cmy_message\u003e \u003e(...);\n...\nauto json = json_dto::to_json(messages);\n```\n\n\nA new example tutorial17 added. This example shows the usage of new features.\n\nAn important note about support for `std::multiset`, `std::unordered_multiset`,\n`std::multimap` and `std::unordered_multimap`: those containers are also supported.\nBut json_dto doesn't do any checks for duplicate keys. In that aspect, json_dto relies\non RapidJSON behavior. For example, if an instance of `std::multimap` contains several\nvalues for some key all those values will be serialized.\nWhat happens to those values is dependent on RapidJSON.\n\n## v.0.2.7\n\nTwo new forms of `from_json` added. It is possible now to deserialize a DTO from already parsed document. For example:\n\n```cpp\nstruct update_period {\n\t...\n\ttemplate\u003ctypename Json_Io\u003e void json_io(Json_Io \u0026 io) {...}\n};\nstruct read_sensor {\n\t...\n\ttemplate\u003ctypename Json_Io\u003e void json_io(Json_Io \u0026 io) {...}\n};\n...\nvoid parse_and_handle_message( const std::string \u0026 raw_msg )\n{\n\trapidjson::Document whole_msg;\n\twhole_msg.Parse\u003c rapidjson::kParseDefaultFlags \u003e( raw_msg );\n\tif( whole_msg.HasParseError() )\n\t\tthrow std::runtime_error(\n\t\t\t\tstd::string{ \"unable to parse message: \" } +\n\t\t\t\trapidjson::GetParseError_En( whole_msg.GetParseError() ) );\n\n\tconst std::string msg_type = whole_msg[ \"message_type\" ].GetString();\n\tconst auto \u0026 payload = whole_msg[ \"payload\" ];\n\tif( \"Update-Period\" == msg_type )\n\t{\n\t\tauto dto = json_dto::from_json\u003c update_period \u003e( payload );\n\t\t...\n\t}\n\telse if( \"Read-Sensor\" == msg_type )\n\t{\n\t\tauto dto = json_dto::from_json\u003c read_sensor \u003e( payload );\n\t\t...\n\t}\n\telse\n\t\t...\n}\n```\n\nFix: compilation problems on FreeBSD 12 with clang-6.0.1.\n\n## v.0.2.6.2\n\nFix: add check for reading fields of DTO to ensure that a source JSON value is of type Object.\n\n## v.0.2.6.1\n\nImprove `std::optional` availability check.\n\n## v.0.2.6\n\nSupport for `std::vector` for `json_dto::to_json` and `json_dto::from_json`\nfunctions.\n\n## v.0.2.5\n\nModify cmake-scripts for vcpkg port (target name `json-dto::json-dto`).\n\n## v.0.2.4\n\nAdd cmake support.\n\nMake string value setter independent to `RAPIDJSON_HAS_STDSTRING`.\n\n## v.0.2.3\n\nBug fix in support of `std::vector\u003cbool\u003e`.\n\n## v.0.2.2\n\nBug fix in implementation of `std::optional` support.\n\nNew example tutorial6.1 added.\n\n## v.0.2.1\n\nSome code style changes to meet expectations of some users.\n\n## v.0.2.0\n\nNew format of `read_json_value` function. **NOTE: this is a breaking change!**\n\nSupport for `std::optional` (and `std::experimental::optional`) added. Note: this\nmay require to specify C++17 standard in compiler params (like `/std:c++17` for MSVC or\n`-std=c++17` for GCC).\n\n# Obtain and build\n\n## Prerequisites\n\nTo use *json_dto* it is necessary to have:\n\n* C++14 compiler (VC++15.0, GCC 5.4 or above, clang 4.0 or above)\n* [rapidjson](https://github.com/miloyip/rapidjson)\n\nAnd for building with mxxru:\n\n* [rapidjson_mxxru](https://github.com/Stiffstream/rapidjson_mxxru)\n\t(v.1.0.0 or above)\n* [Mxx_ru](https://sourceforge.net/projects/mxxru/) 1.6.13 or above\n\nAnd for running test:\n\n* [CATCH2](https://github.com/catchorg/Catch2) 2.5.0 or above\n\n## Obtaining\n\nAssuming that *Git* and *Mxx_ru* are already installed.\n\n### Cloning of Git Repository\n\n```\ngit clone https://github.com/Stiffstream/json_dto.git\n```\n\nAnd then:\n```\ncd json_dto-0.2\nmxxruexternals\n```\nto download and extract *json_dto*'s dependencies.\n\n### MxxRu::externals recipe\n\nFor *json_dto* itself:\n```ruby\nMxxRu::arch_externals :json_dto do |e|\n  e.url 'https://github.com/Stiffstream/json_dto/archive/v.0.2.8.1.tar.gz'\n\n  e.map_dir 'dev/json_dto' =\u003e 'dev'\nend\n```\n\nFor *rapidjson* and *rapidjson_mxxru* dependencies:\n\n```ruby\nMxxRu::arch_externals :rapidjson do |e|\n  e.url 'https://github.com/miloyip/rapidjson/archive/v1.1.0.zip'\n\n  e.map_dir 'include/rapidjson' =\u003e 'dev/rapidjson/include'\nend\n\nMxxRu::arch_externals :rapidjson_mxxru do |e|\n  e.url 'https://github.com/Stiffstream/rapidjson_mxxru/archive/v.1.0.1.tar.gz'\n\n  e.map_dir 'dev/rapidjson_mxxru' =\u003e 'dev'\nend\n```\n\n## Build\n\nWhile *json_dto* is header-only library test and samples require a build.\n\nCompiling with Mxx_ru:\n\n```\ngit clone https://github.com/Stiffstream/json_dto\ncd json_dto\nmxxruexternals\ncd dev\nruby build.rb\n```\n\n*NOTE.* It might be necessary to set up `MXX_RU_CPP_TOOLSET` environment variable,\nsee Mxx_ru documentation for further details.\n\n# How to use it?\n\n**An important notice:** if you do not use Mxx_ru for building your project then\nadd the following defines for your project:\n\n```C++\nRAPIDJSON_HAS_STDSTRING\nRAPIDJSON_HAS_CXX11_RVALUE_REFS\n```\n\nIf you use Mxx_ru and `rapidjson_mxxru/prj.rb` then these definitions will be\nadded automatically.\n\n## Getting started\n\nTo start using *json_dto* simply include `\u003cjson_dto/pub.hpp\u003e` header.\n\nThe usage principle of *json_dto* is borrowed from\n[Boost serialization](http://www.boost.org/doc/libs/1_61_0/libs/serialization/doc/tutorial.html)\nwhere *rapidjson::Value* plays the role of archive.\n\nLet's assume we have a c++ structure that must be serialized to JSON\nand deserialized from JSON:\n\n```C++\nstruct message_t\n{\n\tstd::string m_from;\n\tstd::int64_t m_when;\n\tstd::string m_text;\n};\n```\n\nFor integrating this struct with *json_dto* facilities the struct must be\nmodified as follows:\n\n```C++\nstruct message_t\n{\n\tstd::string m_from;\n\tstd::int64_t m_when;\n\tstd::string m_text;\n\n\t// Entry point for json_dto.\n\ttemplate\u003ctypename Json_Io\u003e\n\tvoid json_io(Json_Io \u0026 io)\n\t{\n\t\tio \u0026 json_dto::mandatory(\"from\", m_from)\n\t\t\t\u0026 json_dto::mandatory(\"when\", m_when)\n\t\t\t\u0026 json_dto::mandatory(\"text\", m_text);\n\t}\n};\n```\n\nHere `json_io()` function is an entry point for *json_dto* library.\nIt describes how to read the data from *rapidjson::Value*\n(that is usualy parsed from string) and how to set the data\nin *rapidjson::Value*.\n`json_io()` is a template function. It allows to have a single\ndescription for read and write operations.\nThe template is instantiated with `Json_Io=json_dto::json_input_t`\nfor reading dto from JSON-value and `Json_Io=json_dto::json_output_t` for writing\ndto to JSON-value. Both `json_dto::json_input_t` and `json_dto::json_output_t`\noverride `operator\u0026` for splitting io functionality.\n\nThere are also iostream-like overrides for `operator\u003c\u003c` and `operator\u003e\u003e`:\n\n```C++\ntemplate\u003ctypename Dto\u003e\njson_input_t \u0026\noperator\u003e\u003e(json_input_t \u0026 i, Dto \u0026 v);\n\ntemplate\u003ctypename Dto\u003e\ninline json_output_t \u0026\noperator\u003c\u003c(json_output_t \u0026 o, const Dto \u0026 v);\n```\n\nBut they are only helpful for top level read/write operations.\n\nIn general *json_dto* gets data from `rapidjson::Value` and puts\nthe data into `rapidjson::Value`. So read/write operations look like this:\n\n```C++\n// Read\nrapidjson::Document document;\n\n// ...\n\njson_dto::json_input_t jin{ document };\n\nmessage_t msg;\njin \u003e\u003e msg;\n\n// If no exceptions were thrown DTO contains data received from JSON.\n```\n\n```C++\n// Write\nrapidjson::Document document;\n\n// ...\n\njson_dto::json_output_t jout{ document, document.GetAllocator() };\n\nconst message_t msg = get_message();\njout \u003c\u003c msg;\n\n// If no exceptions were thrown document contains data received from DTO.\n```\n\nBut usually it is enough to work with `std::string` objects, so *json_dto*\ncomes with handy to/from string helpers:\n\n```C++\ntemplate\u003ctypename Dto\u003e\nstd::string\nto_json(const Dto \u0026 dto);\n\ntemplate\u003ctypename Type\u003e\nType\nfrom_json(const std::string \u0026 json);\n```\n\n[See full example](./dev/sample/tutorial1/main.cpp).\n\n[See full example without to/from string helpers](./dev/sample/tutorial1.1/main.cpp).\n\n## Non intrusive `json_io()`\n\nWhen it is unwanted to add an extra function to C++ structure\nit is possible to use a non intrusive `json_io()` version.\nIn previous example dto part will look like this:\n\n```C++\nstruct message_t\n{\n\tstd::string m_from;\n\tstd::int64_t m_when;\n\tstd::string m_text;\n};\n\nnamespace json_dto\n{\n\ntemplate\u003ctypename Json_Io\u003e\nvoid json_io(Json_Io \u0026 io, message_t \u0026 msg)\n{\n\tio \u0026 json_dto::mandatory(\"from\", msg.m_from)\n\t\t\u0026 json_dto::mandatory(\"when\", msg.m_when)\n\t\t\u0026 json_dto::mandatory(\"text\", msg.m_text);\n}\n\n} /* namespace json_dto */\n```\n\n[See full example](./dev/sample/tutorial2/main.cpp).\n\n**Note** that it is necessary to define `json_io()` in namespace `json_dto`.\n\n## Supported field types\n\nOut of the box *json_dto* lib supports following types:\n\n* Bool: bool;\n* Numeric:\n\tstd::int8_t, std::uint8_t,\n\tstd::int16_t, std::uint16_t,\n\tstd::int32_t, std::uint32_t,\n\tstd::int64_t, std::uint64_t,\n\tdouble;\n* Strings: std::string\n* C++17 specific: std::optional (or std::experimental::optional)\n\nExample:\n\n```C++\nstruct supported_types_t\n{\n\tbool m_bool{ false };\n\n\tstd::int8_t m_int8{};\n\tstd::uint8_t m_uint8{};\n\n\tstd::int16_t m_int16{};\n\tstd::uint16_t m_uint16{};\n\n\tstd::int32_t m_int32{};\n\tstd::uint32_t m_uint32{};\n\n\tstd::int64_t m_int64{};\n\tstd::uint64_t m_uint64{};\n\tdouble m_double{};\n\n\tstd::string m_string{};\n};\n\nnamespace json_dto\n{\n\ntemplate\u003ctypename Json_Io\u003e\nvoid json_io(Json_Io \u0026 io, supported_types_t \u0026 obj)\n{\n\tio \u0026 json_dto::mandatory(\"bool\", obj.m_bool)\n\t\t\u0026 json_dto::mandatory(\"int8\", obj.m_int8)\n\t\t\u0026 json_dto::mandatory(\"uint8\", obj.m_uint8)\n\t\t\u0026 json_dto::mandatory(\"int16\", obj.m_int16)\n\t\t\u0026 json_dto::mandatory(\"uint16\", obj.m_uint16)\n\t\t\u0026 json_dto::mandatory(\"int32\", obj.m_int32)\n\t\t\u0026 json_dto::mandatory(\"uint32\", obj.m_uint32)\n\t\t\u0026 json_dto::mandatory(\"int64\", obj.m_int64)\n\t\t\u0026 json_dto::mandatory(\"uint64\", obj.m_uint64)\n\t\t\u0026 json_dto::mandatory(\"double\", obj.m_double)\n\t\t\u0026 json_dto::mandatory(\"string\", obj.m_string);\n}\n\n} /* namespace json_dto */\n```\n\n[See full example](./dev/sample/tutorial3/main.cpp)\n\n## Mandatory and optional fields\n\nEach data member (at least those of them which are considered to be present in JSON)\nin C++ struct binds to JSON field. Bind can be mandatory or optional.\nOptional bind is extended with default value, but it is also possible\nto set optional fields without defaults.\nAlso it is possible to add a value validator to the bind.\n\nBinds are created by `mandatory()`, `optional()` and\n`optional_no_default()` functions. These functions returns a *field binder*.\n*Binder* is an instantiation of `binder_t` template class\nwhich carries a part of internal logic\ncapable for handling field input/output operations.\nWith the help of *binders* `Json_Io` object understands how read, write and validate\nthe underlying field.\n\n### Mandatory fields\n\nBinders for mandatory fields are created via `mandatory()` function:\n\n```C++\ntemplate\u003c\n\t\ttypename Field_Type,\n\t\ttypename Validator = empty_validator_t\u003e\nauto mandatory(\n\tstring_ref_t field_name,\n\tField_Type \u0026 field,\n\tValidator validator = Validator{});\n\n// Since v.0.2.10\ntemplate\u003c\n\t\ttypename Reader_Writer,\n\t\ttypename Field_Type,\n\t\ttypename Validator = empty_validator_t\u003e\nauto mandatory(\n\tReader_Writer reader_writer,\n\tstring_ref_t field_name,\n\tField_Type \u0026 field,\n\tValidator validator = Validator{});\n```\n\nThe parameter *field_name* is of type `string_ref_t`\nwhich is an alias for `rapidjson::Value::StringRefType`.\nTypically it is enough to pass `std::string` or `char *` args\n(see *rapidjson*\n[documentation](http://rapidjson.org/classrapidjson_1_1_generic_value.html)\nfor further details).\nThe parameter *field* is a reference to the instance of the field value.\nThe parameter *validator* is optional and it sets validator on fields value.\nValidators will be described later. By default `empty_validator_t`\nis used, and as it says it does nothing.\n\n### Optional fields\n\nBinders for optional fields are created via `optional()` and\n`optional_no_default()` functions:\n\n```C++\ntemplate\u003c\n\t\ttypename Field_Type,\n\t\ttypename Field_Default_Value_Type,\n\t\ttypename Validator = empty_validator_t\u003e\nauto optional(\n\tstring_ref_t field_name,\n\tField_Type \u0026 field,\n\tField_Default_Value_Type default_value,\n\tValidator validator = Validator{});\n\ntemplate\u003c\n\t\ttypename Field_Type,\n\t\ttypename Validator = empty_validator_t\u003e\nauto optional_no_default(\n\tstring_ref_t field_name,\n\tField_Type \u0026 field,\n\tValidator validator = Validator{});\n\n// Since v.0.2.10\ntemplate\u003c\n\t\ttypename Reader_Writer,\n\t\ttypename Field_Type,\n\t\ttypename Field_Default_Value_Type,\n\t\ttypename Validator = empty_validator_t\u003e\nauto optional(\n\tReader_Writer reader_writer,\n\tstring_ref_t field_name,\n\tField_Type \u0026 field,\n\tField_Default_Value_Type default_value,\n\tValidator validator = Validator{});\n\n// Since v.0.2.10\ntemplate\u003c\n\t\ttypename Reader_Writer,\n\t\ttypename Field_Type,\n\t\ttypename Validator = empty_validator_t\u003e\nauto optional_no_default(\n\tReader_Writer reader_writer,\n\tstring_ref_t field_name,\n\tField_Type \u0026 field,\n\tValidator validator = Validator{});\n```\n\nParameters for functions are pretty much the same as for\n`mandatory()` functon.\n\nThe only difference is the third parameter for `optional()` function,\nit defines default value for a field if it is not defined in JSON.\n\nIn case of reading DTO, if optional field has default value\nand JSON object doesn't define this field then default value is used.\nIn case of writing DTO, if value equals to default\nthen this field wouldn't be included in JSON.\n\nFor `optional()` there is a partial specification that accepts\n`nullptr` argument as *default_value* parameter, it is usefull for\n`nullable_t\u003cT\u003e` fields.\n\nExample of using optional fields:\n\n```C++\nstruct message_t\n{\n\tstd::string m_from;\n\tstd::int64_t m_when;\n\tstd::string m_text;\n\tstd::string m_text_format;\n\tbool m_is_private{ false };\n};\n\nnamespace json_dto\n{\n\ntemplate\u003ctypename Json_Io\u003e\nvoid json_io(Json_Io \u0026 io, message_t \u0026 msg)\n{\n\tio \u0026 json_dto::mandatory(\"from\", msg.m_from)\n\t\t\u0026 json_dto::mandatory(\"when\", msg.m_when)\n\t\t\u0026 json_dto::mandatory(\"text\", msg.m_text)\n\t\t\u0026 json_dto::optional(\"text_format\", msg.m_text_format, \"text/plain\")\n\t\t\u0026 json_dto::optional_no_default(\"is_private\", msg.m_is_private);\n}\n\n} /* namespace json_dto */\n```\n\n[See full example](./dev/sample/tutorial4/main.cpp)\n\n#### Optional fields and std::optional\n\nSince v.0.2 it is possible to use C++17's `std::optional` template as a type\nfor field. In this case `std::nullopt` can be passed as third argument to\n`json_dto::optional()` function:\n\n```C++\nstruct email_data_t\n{\n\tstd::string m_from;\n\tstd::string m_to;\n\tstd::string m_subject;\n\tstd::optional\u003cstd::vector\u003cstd::string\u003e\u003e m_cc;\n\tstd::optional\u003cstd::vector\u003cstd::string\u003e\u003e m_bcc;\n\t...\n\ttemplate\u003ctypename Json_Io\u003e\n\tvoid json_io(Json_Io \u0026 io)\n\t{\n\t\tio \u0026 json_dto::mandatory(\"from\", m_from)\n\t\t\t\u0026 json_dto::mandatory(\"to\", m_to)\n\t\t\t\u0026 json_dto::mandatory(\"subject\", m_subject)\n\t\t\t\u0026 json_dto::optional(\"cc\", m_cc, std::nullopt)\n\t\t\t\u0026 json_dto::optional(\"bcc\", m_bcc, std::nullopt)\n\t\t\t...\n\t}\n};\n```\n\n*Note.* If a compiler doesn't have `std::optional` but have\n`std::experimental::optional` then `std::experimental::optional` and\n`std::experimental::nullopt` can be used.\n\n\n## Array support\n\n### Array fields\n\nJSON arrays are supported by *json_dto*, but there is one very important\nlimitation: all elements of the array must have the same type.\nTo set up an array simply use `std::vector\u003cT\u003e`.\nIf DTO member is of `std::vector\u003cT\u003e` type,\nthen corresponding JSON field is considered to be an array.\nWhile for output the elements of the array-field will be automatically\nof the same type, for successful input it is mandatory\nthat all elements of the array are convertible to vector value type.\n\nExample for array-fields:\n\n```C++\nstruct vector_types_t\n{\n\tstd::vector\u003cbool\u003e m_bool{};\n\n\tstd::vector\u003cstd::int8_t\u003e m_int8{};\n\tstd::vector\u003cstd::uint8_t\u003e m_uint8{};\n\n\tstd::vector\u003cstd::int16_t\u003e m_int16{};\n\tstd::vector\u003cstd::uint16_t\u003e m_uint16{};\n\n\tstd::vector\u003cstd::int32_t\u003e m_int32{};\n\tstd::vector\u003cstd::uint32_t\u003e m_uint32{};\n\n\tstd::vector\u003cstd::int64_t\u003e m_int64{};\n\tstd::vector\u003cstd::uint64_t\u003e m_uint64{};\n\tstd::vector\u003cdouble\u003e m_double{};\n\n\tstd::vector\u003cstd::string\u003e m_string{};\n};\n\nnamespace json_dto\n{\n\ntemplate\u003ctypename Json_Io\u003e\nvoid json_io(Json_Io \u0026 io, vector_types_t \u0026 obj)\n{\n\tio \u0026 json_dto::mandatory(\"bool\", obj.m_bool)\n\t\t\u0026 json_dto::mandatory(\"int8\", obj.m_int8)\n\t\t\u0026 json_dto::mandatory(\"uint8\", obj.m_uint8)\n\t\t\u0026 json_dto::mandatory(\"int16\", obj.m_int16)\n\t\t\u0026 json_dto::mandatory(\"uint16\", obj.m_uint16)\n\t\t\u0026 json_dto::mandatory(\"int32\", obj.m_int32)\n\t\t\u0026 json_dto::mandatory(\"uint32\", obj.m_uint32)\n\t\t\u0026 json_dto::mandatory(\"int64\", obj.m_int64)\n\t\t\u0026 json_dto::mandatory(\"uint64\", obj.m_uint64)\n\t\t\u0026 json_dto::mandatory(\"double\", obj.m_double)\n\t\t\u0026 json_dto::mandatory(\"string\", obj.m_string);\n}\n\n} /* namespace json_dto */\n```\n\n[See full example](./dev/sample/tutorial5/main.cpp)\n\n### Arrays and to_json and from_json\n\nSince v.0.2.6 it is possible to serialize array of objects into JSON\nby `json_dto::to_json` function. It is also possible to deserialize\nJSON with array of objects into `std::vector` by `json_dto::from_json`\nfunction. For example:\n\n```C++\n#include \u003cjson_dto/pub.hpp\u003e\n\n#include \u003ciostream\u003e\n#include \u003calgorithm\u003e\n\nstruct data_t {\n\tstd::string m_key;\n\tint m_value;\n\n\ttemplate\u003ctypename Json_Io\u003e\n\tvoid json_io(Json_Io \u0026 io) {\n\t\tio \u0026 json_dto::mandatory(\"key\", m_key)\n\t\t\t\u0026 json_dto::mandatory(\"value\", m_value);\n\t}\n};\n\nint main() {\n\tconst std::string json_data{\n\t\tR\"JSON(\n\t\t\t[{\"key\":\"first\", \"value\":32},\n\t\t\t {\"key\":\"second\", \"value\":15},\n\t\t\t {\"key\":\"third\", \"value\":80}]\n\t\t)JSON\"\n\t};\n\n\tauto data = json_dto::from_json\u003c std::vector\u003cdata_t\u003e \u003e(json_data);\n\tstd::sort(data.begin(), data.end(),\n\t\t[](const auto \u0026 a, const auto \u0026 b) { return a.m_value \u003c b.m_value; });\n\n\tstd::cout \u003c\u003c \"Sorted data: \" \u003c\u003c json_dto::to_json(data) \u003c\u003c std::endl;\n}\n```\n\n## Other types of containers\n\nSince v.0.2.8 there is a support for STL containers like `std::deque`, `std::list`,\n`std::forward_list`, `std::set`, `std::unordered_set`, `std::map` and `std::unordered_map`.\nThose types can be used as types of fields of serialized struct/classes:\n\n```cpp\n#include \u003cjson_dto/pub.hpp\u003e\n\n#include \u003cdeque\u003e\n#include \u003cset\u003e\n#include \u003cmap\u003e\n\nstruct my_message {\n\tstd::deque\u003cint\u003e ids_;\n\tstd::set\u003cstd::string\u003e tags_;\n\tstd::map\u003cstd::string, some_another_type\u003e props_;\n\t...\n\ttemplate\u003ctypename Json_Io\u003e\n\tvoid json_io(Json_Io \u0026 io) {\n\t\tio \u0026 json_dto::mandatory(\"ids\", ids_)\n\t\t\t\u0026 json_dto::mandatory(\"tags\", tags_)\n\t\t\t\u0026 json_dto::mandatory(\"properties\", props_)\n\t\t\t...\n\t\t\t;\n\t}\n};\n```\n\nAlso STL containers are supported by `json_dto::from_json()` and `json_dto::to_json()` functions:\n\n```cpp\nauto messages = json_dto::from_json\u003c std::forward_list\u003cmy_message\u003e \u003e(...);\n...\nauto json = json_dto::to_json(messages);\n```\n\n[See a special example with usage of STL containers](./dev/sample/tutorial17/main.cpp)\n\nNote that support for those STL-containers is not hardcoded in json_dto. \nInstead, json_dto tries to detect a type of a container by inspecting the presence of types\nlike `value_type`, `key_type`, `mapped_type` and methods like `begin()/end()`, `emplace()`,\n`emplace_back()` and so on. It means that json_dto may work not only with STL-containers but\nwith other containers those mimics like STL-containers.\n\n**Note.** Type `std::array` is not supported now. If you have to deal with `std::array` and\nwant to have a support of it in json_dto please\n[open an issue](https://github.com/stiffstream/json_dto/issues)\nand we'll discuss some corner cases related to `std::array`.\n\n### Multimaps and multisets\n\nAn important note about support for `std::multiset`, `std::unordered_multiset`,\n`std::multimap` and `std::unordered_multimap`: those containers are also supported.\nBut json_dto doesn't do any checks for duplicate keys. In that aspect, json_dto relies\non RapidJSON behavior. For example, if an instance of `std::multimap` contains several\nvalues for some key all those values will be serialized.\nWhat happens to those values is dependent on RapidJSON.\n\n## Nullable fields\n\nTo support JSON null values, *json_dto* introduces `nullable_t\u003cT\u003e`.\nIt is required that nullable field is explicitly defined as\ndata member of type `nullable_t\u003cT\u003e`.\n\nInterface of `nullable_t\u003cT\u003e` tries to mimic `std::optional` interface.\n\nExample for `nullable_t\u003cT\u003e` field:\n\n```C++\nstruct message_t\n{\n\tmessage_t() {}\n\n\tmessage_t(\n\t\tstd::string from,\n\t\tstd::int64_t when,\n\t\tstd::string text)\n\t\t:\tm_from{ std::move(from) }\n\t\t,\tm_when{ when }\n\t\t,\tm_text{ std::move(text) }\n\t{}\n\n\tstd::string m_from;\n\tstd::int64_t m_when;\n\tstd::string m_text;\n\n\t// Log level.\n\t// By default is constructed with null value.\n\tjson_dto::nullable_t\u003cstd::int32_t\u003e m_log_level{};\n};\n\nnamespace json_dto\n{\n\ntemplate\u003ctypename Json_Io\u003e\nvoid json_io(Json_Io \u0026 io, message_t \u0026 msg)\n{\n\tio \u0026 json_dto::mandatory(\"from\", msg.m_from)\n\t\t\u0026 json_dto::mandatory(\"when\", msg.m_when)\n\t\t\u0026 json_dto::mandatory(\"text\", msg.m_text)\n\t\t\u0026 json_dto::optional(\"log_level\", msg.m_log_level, nullptr);\n}\n\n} /* namespace json_dto */\n\nvoid\nsome_function( ... )\n{\n\t// ...\n\tauto msg = json_dto::from_json\u003cmessage_t\u003e(json_data);\n\n\t// ...\n\n\t// If field is defined then its value can be obtained and used.\n\tif( msg.m_log_level )\n\t\tuse_value(*msg.m_log_level);\n\n\t// ...\n\n\tmsg.m_log_level = 1; // Set new value.\n\n\t// ...\n\n\t// equivalent to msg.m_log_level.reset();\n\tmsg.m_log_level = nullptr; // Reset value.\n\n\t// ...\n}\n```\n\n[See full example](./dev/sample/tutorial6/main.cpp)\n\nHere default value for optional nullble field is `nullptr`.\nAnd it means that absence of value is a default state for a field.\nSo when converting to JSON no-value nullable field\nwouldn't be included in JSON as `\"field\":null` piece.\n\nNullable fields can be used with arrays:\n\n```C++\nstruct message_t\n{\n\tmessage_t() {}\n\n\tmessage_t(\n\t\tstd::string from,\n\t\tstd::int64_t when,\n\t\tstd::string text)\n\t\t:\tm_from{ std::move(from) }\n\t\t,\tm_when{ when }\n\t\t,\tm_text{ std::move(text) }\n\t{}\n\n\t// Who sent a message.\n\tstd::string m_from;\n\n\t// When the message was sent (unixtime).\n\tstd::int64_t m_when;\n\n\t// Message text.\n\tstd::string m_text;\n\n\t// Log level.\n\t// By default is constructed with null value.\n\tjson_dto::nullable_t\u003cstd::int32_t\u003e m_log_level{};\n\n\tjson_dto::nullable_t\u003c std::vector\u003cstd::string\u003e \u003e m_tags{};\n};\n\nnamespace json_dto\n{\n\ntemplate\u003ctypename Json_Io\u003e\nvoid json_io(Json_Io \u0026 io, message_t \u0026 msg)\n{\n\tio \u0026 json_dto::mandatory(\"from\", msg.m_from)\n\t\t\u0026 json_dto::mandatory(\"when\", msg.m_when)\n\t\t\u0026 json_dto::mandatory(\"text\", msg.m_text)\n\t\t\u0026 json_dto::optional(\"log_level\", msg.m_log_level, nullptr)\n\t\t\u0026 json_dto::optional(\"tags\", msg.m_tags, nullptr);\n}\n\n} /* namespace json_dto */\n\nvoid some_function( ... )\n{\n\t// ...\n\tauto msg = json_dto::from_json\u003cmessage_t\u003e(json_data);\n\n\t// ...\n\n\tif( msg.m_tags )\n\t\tuse_tags(*msg.m_tags);\n\n\t// ...\n}\n\nvoid some_other_function( ... )\n{\n\tmessage_t msg{ ... };\n\t// ...\n\n\t// Add tags:\n\tmsg.m_tags.emplace(); // equivalent to msg = std::vector\u003cstd::string\u003e{};\n\tmsg.m_tags-\u003eemplace_back(\"sample\");\n\tmsg.m_tags-\u003eemplace_back(\"tutorial\");\n\n\t// ...\n}\n```\n\n[See full example](./dev/sample/tutorial7/main.cpp)\n\n## Complex types\n\n*json_dto* allows to construct complex types with nested objects.\nUsing nested objects is pretty much the same as using data of a simple types.\nNested objects can be optional, nullable and be elements of array-fields.\nHowever there are some constraints:\n\n* nested type must be itself integrated with *json_dto*;\n* type must be default-constructible (for input);\n* for optional fields with default value equality operator must be defined\n(more precisely an equality operator between nested type and type of passed default value).\n\nSuppose there is a type which is already integrated with *json_dto*:\n\n```C++\nstruct message_source_t\n{\n\tstd::int32_t m_thread_id{ 0 };\n\tstd::string m_subsystem{};\n\n\ttemplate\u003ctypename Json_Io\u003e\n\tvoid json_io(Json_Io \u0026 io)\n\t{\n\t\tio \u0026 json_dto::optional(\"thread_id\", m_thread_id, 0)\n\t\t\t\u0026 json_dto::mandatory(\"subsystem\", m_subsystem);\n\t}\n};\n```\n\nThen it can be used as a nested object in other type:\n\n```C++\nstruct message_t\n{\n\tmessage_source_t m_from;\n\tstd::int64_t m_when;\n\tstd::string m_text;\n\n\ttemplate\u003ctypename Json_Io\u003e\n\tvoid json_io(Json_Io \u0026 io)\n\t{\n\t\tio \u0026 json_dto::mandatory(\"from\", m_from) // Exactly as with simple types.\n\t\t\t\u0026 json_dto::mandatory(\"when\", m_when)\n\t\t\t\u0026 json_dto::mandatory(\"text\", m_text);\n\t}\n};\n```\n\n[See full example](./dev/sample/tutorial8/main.cpp)\n\n[And see full example using nested objects as nullable and arrays](./dev/sample/tutorial9/main.cpp)\n\n## Inheritance\n\n*json_dto* works well with inheritance. It is possible to use\nbase implementation of `json_io()` function or completely override it.\n\nFor example derived class can use base class like this:\n\n```C++\nstruct derived_t : public base_t\n{\n\t//...\n\n\ttemplate\u003ctypename Json_Io\u003e\n\tvoid json_io(Json_Io \u0026 io)\n\t{\n\t\tbase_t::json_io(io); // Run io on base class.\n\n\t\t// Run io on extra data:\n\t\tio \u0026 json_dto::mandatory(\"some_field\", m_some_field)\n\t\t\t// ...\n\t\t\t;\n\t}\n};\n```\n\nHowever for easier maintenance it is recommended to use non intrusive\n`json_io()` function. Because if base class is integrated with *json_dto*\nin non intrusive manner, then the following wouldn't work:\n\n```C++\n\ttemplate\u003ctypename Json_Io\u003e\n\tvoid json_io(Json_Io \u0026 io)\n\t{\n\t\t// Base class doesn't provide such member function.\n\t\tbase_t::json_io(io); // Run io on base class.\n\t\t// ...\n\t}\n```\n\nSo it is preferred to put inheritance this way:\n\n```C++\nstruct message_source_t\n{\n\tstd::int32_t m_thread_id{ 0 };\n\tstd::string m_subsystem{};\n};\n\nnamespace json_dto\n{\n\ntemplate\u003ctypename Json_Io\u003e\nvoid json_io(Json_Io \u0026 io, message_source_t \u0026 m)\n{\n\tio \u0026 json_dto::optional(\"thread_id\", m.m_thread_id, 0)\n\t\t\u0026 json_dto::mandatory(\"subsystem\", m.m_subsystem);\n}\n\n} /* namespace json_dto */\n\nstruct message_t : public message_source_t\n{\n\tstd::int64_t m_when;\n\tstd::string m_text;\n\n\ttemplate\u003ctypename Json_Io\u003e\n\tvoid json_io(Json_Io \u0026 io)\n\t{\n\t\tjson_dto::json_io(io, static_cast\u003cmessage_source_t \u0026\u003e(*this));\n\n\t\tio \u0026 json_dto::mandatory(\"when\", m_when)\n\t\t\t\u0026 json_dto::mandatory(\"text\", m_text);\n\t}\n};\n```\n\n[See full example](./dev/sample/tutorial10/main.cpp)\n\n## Validators\n\n*json_dto* allows to set validator on each field.\nValidator is a function object (an object of a type supporting an\n`operator()` member function) that receives a single parameter.\n\nWhen handling input *json_dto* calls specified validator\nand passes resulting field value as an argument.\nIf validator returns without throwing exception,\nthen field value considered to be valid, and execution continues.\nOtherwise exception is catched and another will be thrown:\n`json_dto::ex_t`. This exeption contains original exception description\nsupplemented with field name information.\n\nWhen handling ouput *json_dto* calls specified validator before\ntrying to assign field value of JSON object. In all other respects\nvalidation is the same as for input.\n\nA simple example of using validators:\n\n```C++\nvoid check_all_7bit(\n\tconst std::string \u0026 text)\n{\n\tconst auto it = std::find_if(std::begin(text), std::end(text),\n\t\t\t[](char c){ return c \u0026 0x80; });\n\n\tif( std::end(text) != it )\n\t{\n\t\tthrow std::runtime_error{\n\t\t\t\"non 7bit char at pos \" +\n\t\t\tstd::to_string(std::distance(std::begin(text), it)) };\n\t}\n}\n\nstruct message_t\n{\n\tstd::string m_from;\n\tstd::int64_t m_when;\n\n\t// Message text. Must be 7bit ascii.\n\tstd::string m_text;\n\n\ttemplate\u003ctypename Json_Io\u003e\n\tvoid json_io(Json_Io \u0026 io)\n\t{\n\t\tio \u0026 json_dto::mandatory(\"from\", m_from)\n\t\t\t\u0026 json_dto::mandatory(\"when\", m_when)\n\t\t\t\u0026 json_dto::mandatory(\"text\", m_text, check_all_7bit);\n\t}\n};\n```\n\n[See full example](./dev/sample/tutorial11/main.cpp)\n\n### Standard validators\n\n*json_dto* comes with some useful ready to use validators for simple types.\nThey are defined in `\u003cjson_dto/pub.hpp\u003e` header.\n\nStandard validators curently available:\n\n* `min_max_constraint_t\u003cNum\u003e` - range validator, targeted for numeric types;\n* `one_of_validator_t\u003cT\u003e` - validator for set of values.\n\nStandard validators are template classes with overloaded `operator()`.\nAnd as they are template classes so for convenience\nfor each validator there is an auxiliary function that helps deduce\ntype of template instance from arguments:\n\n```C++\ntemplate\u003ctypename Number\u003e\nauto min_max_constraint(Number min_value, Number max_value);\n\ntemplate\u003ctypename Field_Type\u003e\nauto one_of_constraint(std::initializer_list\u003cField_Type\u003e values);\n```\n\n[See full example with standard validators](./dev/sample/tutorial12/main.cpp)\n\n## Representing several fields inside an array\n\nSometimes several values may be stored inside an array:\n\n```json\n{ \"x\": [1, \"Hello!\", 0.3] }\n```\n\nSince v.0.3.3 json_dto support such cases the following way:\n\n```cpp\nstruct inner {\n\tint a;\n\tstd::string b;\n\tdouble c;\n\n\t//NOTE: there is no json_io for `inner` type.\n};\n\nstruct outer {\n\tinner x;\n\n\ttemplate\u003c typename Io \u003e void json_io( Io \u0026 io ) {\n\t\tio \u0026 json_dto::mandatory(\n\t\t\t\t// The use of special reader-writer that will pack\n\t\t\t\t// all described fields into one array value.\n\t\t\t\tjson_dto::inside_array::reader_writer(\n\t\t\t\t\t// All fields to be (de)serialized must be enumerated here.\n\t\t\t\t\t// The order of the enumeration is important: (de)serialization\n\t\t\t\t\t// is performed in this order.\n\t\t\t\t\tjson_dto::inside_array::member( x.a ),\n\t\t\t\t\tjson_dto::inside_array::member( x.b ),\n\t\t\t\t\tjson_dto::inside_array::member( x.c ) ),\n\t\t\t\t\"x\", x );\n\t}\n};\n...\nauto obj = json_dto::from_json\u003couter\u003e( R\"({\"x\":[1, \"Hello!\", 0.3]})\" );\n```\n\nBy default `json_dto::inside_array::reader_writer` expects exact number of\nfields. For example, if three fields are described then an array with exactly tree\nvalues is expected during deserialization, otherwise an exception will be thrown.\nHowever, there may be cases when JSON contains less values. It means that N\nfirst members are mandatory, and all other are optional. This can be expressed\nthat way:\n\n```cpp\nstruct outer {\n\tinner x;\n\n\ttemplate\u003c typename Io \u003e void json_io( Io \u0026 io ) {\n\t\tio \u0026 json_dto::mandatory(\n\t\t\t\tjson_dto::inside_array::reader_writer\u003c\n\t\t\t\t\t// Specify that the first field is mandatory and all remaining\n\t\t\t\t\t// are optional.\n\t\t\t\t\tjson_dto::inside_array::at_least\u003c1\u003e\n\t\t\t\t\u003e(\n\t\t\t\t\t// This is mandatory field,\n\t\t\t\t\tjson_dto::inside_array::member( x.a ),\n\t\t\t\t\t// This is optional field and we specify a default value for\n\t\t\t\t\t// a case when it's missing.\n\t\t\t\t\tjson_dto::inside_array::member_with_default_value( x.b, std::string{ \"Nothing\" } ),\n\t\t\t\t\t// This is optional field and we doesn't specify a default value\n\t\t\t\t\t// for it. It it's missing than `double{}` will be used as a new\n\t\t\t\t\t// value for `x.c`.\n\t\t\t\t\tjson_dto::inside_array::member( x.c ) ),\n\t\t\t\t\"x\", x );\n\t}\n};\n```\n\nThere are a family of `json_dto::inside_array::member` and `json_dto::inside_array::member_with_default_value` functions that allow to specify a custom reader-writer and/or a validator:\n\n```cpp\njson_dto::inside_array::member( x.a, json_dto::min_max_constraint(-10, 10));\njson_dtp::inside_array::member( my_custom_reader_writer{}, x.b );\njson_dtp::inside_array::member( my_custom_reader_writer{}, x.c, json_dto::min_max_constraint(-1, 1) );\n```\n\nThe inside_array functionality can be used for manual support of `std::tuple`:\n\n```cpp\nstruct outer {\n\tstd::tuple\u003cint, std::string, double\u003e x;\n\n\ttemplate\u003c typename Io \u003e void json_io( Io \u0026 io ) {\n\t\tio \u0026 json_dto::mandatory(\n\t\t\t\tjson_dto::inside_array::reader_writer(\n\t\t\t\t\tjson_dto::inside_array::member( std::get\u003c0\u003e(x) ),\n\t\t\t\t\tjson_dto::inside_array::member( std::get\u003c1\u003e(x) ),\n\t\t\t\t\tjson_dto::inside_array::member( std::get\u003c2\u003e(x) ) ),\n\t\t\t\t\"x\", x );\n\t}\n};\n...\nauto obj = json_dto::from_json\u003couter\u003e( R\"({\"x\":[1, \"Hello!\", 0.3]})\" );\n```\n\n## User defined IO\n\n### Overloading of read_json_value and write_json_value\n\nIt is possible to define custom IO logic for a specific type.\nIt might be useful for types when using object is an overkill,\nfor example time point that can be stored in format of 'YYYY.MM.DD hh:mm:ss'\nor some token composed of several small items like '\u003citem1\u003e-\u003citem1\u003e-\u003citem3\u003e'.\nBut introducing custom IO logic for some type\nrequires to work with *rapidjson* API directly.\n\nThere are two way to introduce custom IO logic.\n\nThe first way uses C++'s Argument Dependent Lookup feature:\nan user should define `read_json_value` and `write_json_value` in the same\nnamespace where types are defined. The right implementations of\n`read_json_value` and `write_json_value` will be found by C++ compiler automatically.\nFor example:\n\n```C++\nnamespace importance_levels\n{\n\nenum class level_t\n\t{\n\t\tlow,\n\t\tnormal,\n\t\thigh\n\t};\n\n// read_json_value and write_json_value for level_t are\n// defined in importance_levels namespace.\n// They will be found by argument dependent lookup.\nvoid read_json_value(\n\tlevel_t \u0026 value,\n\tconst rapidjson::Value \u0026 from)\n{...}\n\nvoid write_json_value(\n\tconst level_t \u0026 value,\n\trapidjson::Value \u0026 object,\n\trapidjson::MemoryPoolAllocator\u003c\u003e \u0026 allocator)\n{...}\n\n} /* namespace importance_levels */\n```\n\nThis approach also allows to define `read_json_value` and `write_json_value`\nfor user's template type. For example:\n\n```C++\nnamespace demo\n{\n\ntemplate\u003ctypename T\u003e\nclass some_template\n{...}\n\ntemplate\u003ctypename T\u003e\nvoid read_json_value(\n\tsome_template\u003cT\u003e \u0026 value,\n\tconst rapidjson::Value \u0026 from)\n{...}\n\ntemplate\u003ctypename T\u003e\nvoid write_json_value(\n\tconst some_template\u003cT\u003e \u0026 value,\n\trapidjson::Value \u0026 object,\n\trapidjson::MemoryPoolAllocator\u003c\u003e \u0026 allocator)\n{...}\n\n} /* namespace demo */\n\nstruct my_data_t\n{\n\tdemo::some_template\u003cint\u003e m_first;\n\tdemo::some_template\u003cdouble\u003e m_second;\n\t...\n\ttemplate\u003ctypename Json_Io\u003e\n\tvoid json_io(Json_Io \u0026 io)\n\t{\n\t\tio \u0026 json_dto::mandatory(\"first\", m_first)\n\t\t\t\u0026 json_dto::mandatory(\"second\", m_second)\n\t\t\t...\n\t}\n};\n```\n\n[See full example with custom IO and ADL](./dev/sample/tutorial15/main.cpp)\n\nThe second way uses explicit template specialization for 2 functons\ninside `json_dto` namespace:\n\n```C++\nnamespace json_dto\n{\n\ntemplate\u003c\u003e\nvoid read_json_value(\n\tCustom_Type \u0026 v,\n\tconst rapidjson::Value \u0026 object)\n{\n\t// ...\n}\n\ntemplate\u003c\u003e\nvoid write_json_value(\n\tconst Custom_Type \u0026 v,\n\trapidjson::Value \u0026 object,\n\trapidjson::MemoryPoolAllocator\u003c\u003e \u0026 allocator)\n{\n\t// ...\n}\n\n} /* namespace json_dto */\n```\n\n*json_dto* will consider these specializations for using with\nspecified `Custom_Type`. This way can be used when it is impossible\nto place `read_json_value` and `write_json_value` into the namespace where\nthe type if defined (for example if it is standard type like `std::filesystem::path`).\n\n[See full example with custom IO](./dev/sample/tutorial14/main.cpp)\n\n### Usage of Reader_Writer\n\nSuppose we have an enumeration `log_level` defined such way:\n\n```cpp\nenum class log_level { low, normal, high };\n```\n\nAnd we have two structs that use that `log_level` enumeration:\n\n```cpp\nstruct log_message\n{\n\tlog_level level_;\n\tstd::string msg_;\n};\n\nstruct log_config\n{\n\tstd::string path_;\n\tlog_level level_;\n};\n```\n\nSerialization of `log_level` to JSON should use numeric values of log levels, e.g.: `{\"level\":0, \"msg\":\"...\"}`, but the serialization of `log_config` should use textual names instead of numeric values, e.g.: `{\"path\":\"/var/log/demo\", \"level\":\"low\"}`.\n\nSuch a task can't be implemented by writing overloads of `read_json_value` and `write_json_value` functions. Custom Reader_Writers should be used in that case:\n\n```cpp\nstruct numeric_log_level\n{\n\tvoid read( log_level \u0026 v, const rapidjson::Value \u0026 from ) const\n\t{\n\t\tusing json_dto::read_json_value;\n\n\t\tint actual;\n\t\tread_json_value( actual, from );\n\n\t\tv = static_cast\u003clog_level\u003e(actual);\n\t}\n\n\tvoid write(\n\t\tconst log_level \u0026 v,\n\t\trapidjson::Value \u0026 to,\n\t\trapidjson::MemoryPoolAllocator\u003c\u003e \u0026 allocator ) const\n\t{\n\t\tusing json_dto::write_json_value;\n\n\t\tconst int actual = static_cast\u003cint\u003e(v);\n\t\twrite_json_value( actual, to, allocator );\n\t}\n};\n\nstruct log_message\n{\n\tlog_level level_;\n\tstd::string msg_;\n\n\ttemplate\u003c typename Json_Io \u003e\n\tvoid json_io( Json_Io \u0026 io )\n\t{\n\t\tio \u0026 json_dto::mandatory( numeric_log_level{}, \"level\", level_ )\n\t\t\t\u0026 json_dto::mandatory( \"msg\", msg_ );\n\t}\n};\n\nstruct textual_log_level\n{\n\tvoid read( log_level \u0026 v, const rapidjson::Value \u0026 from ) const\n\t{\n\t\tusing json_dto::read_json_value;\n\n\t\tstd::string str_v;\n\t\tread_json_value( str_v, from );\n\n\t\tif( \"low\" == str_v ) v = log_level::low;\n\t\telse if( \"normal\" == str_v ) v = log_level::normal;\n\t\telse if( \"high\" == str_v ) v = log_level::high;\n\t\telse throw json_dto::ex_t{ \"invalid value for log_level\" };\n\t}\n\n\tvoid write(\n\t\tconst log_level \u0026 v,\n\t\trapidjson::Value \u0026 to,\n\t\trapidjson::MemoryPoolAllocator\u003c\u003e \u0026 allocator ) const\n\t{\n\t\tusing json_dto::write_json_value;\n\t\tusing json_dto::string_ref_t;\n\n\t\tswitch( v )\n\t\t{\n\t\t\tcase log_level::low:\n\t\t\t\twrite_json_value( string_ref_t{ \"low\" }, to, allocator );\n\t\t\tbreak;\n\n\t\t\tcase log_level::normal:\n\t\t\t\twrite_json_value( string_ref_t{ \"normal\" }, to, allocator );\n\t\t\tbreak;\n\n\t\t\tcase log_level::high:\n\t\t\t\twrite_json_value( string_ref_t{ \"high\" }, to, allocator );\n\t\t\tbreak;\n\t\t}\n\t}\n};\n\nstruct log_config\n{\n\tstd::string path_;\n\tlog_level level_;\n\n\ttemplate\u003c typename Json_Io \u003e\n\tvoid json_io( Json_Io \u0026 io )\n\t{\n\t\tio \u0026 json_dto::mandatory( \"path\", path_ )\n\t\t\t\u0026 json_dto::mandatory( textual_log_level{}, \"level\", level_ );\n\t}\n};\n```\n\nNote that Reader_Writer class should have two const methods `read` and `write` those signatures are the same with the signatures of `read_json_value` and `write_json_value` functions.\n\n[See full example with Reader_Writer](./dev/sample/tutorial18/main.cpp)\n\nCustom Reader_Writer classes can also be used for handling non-standard representation of some values in JSON document. For example, sometimes string-values like `\"NAN\"` or `\"nan\"` are used for NaN (Not-a-Number) values. RapidJSON can only parsed special value `NaN`, but not `\"NAN\"` nor `\"nan\"` values. In such case a custom Reader_Writer like the following one can be used:\n\n```cpp\nstruct custom_floating_point_reader_writer\n{\n\ttemplate\u003c typename T \u003e\n\tvoid read( T \u0026 v, const rapidjson::Value \u0026 from ) const\n\t{\n\t\tif( from.IsNumber() )\n\t\t{\n\t\t\tjson_dto::read_json_value( v, from );\n\t\t\treturn;\n\t\t}\n\t\telse if( from.IsString() )\n\t\t{\n\t\t\tconst json_dto::string_ref_t str_v{ from.GetString() };\n\t\t\tif( equal_caseless( str_v, \"nan\" ) )\n\t\t\t{\n\t\t\t\tv = std::numeric_limits\u003cT\u003e::quiet_NaN();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if( equal_caseless( str_v, \"inf\" ) )\n\t\t\t{\n\t\t\t\tv = std::numeric_limits\u003cT\u003e::infinity();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if( equal_caseless( str_v, \"-inf\" ) )\n\t\t\t{\n\t\t\t\tv = -std::numeric_limits\u003cT\u003e::infinity();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tthrow json_dto::ex_t{ \"unable to parse value\" };\n\t}\n\n\ttemplate\u003c typename T \u003e\n\tvoid write(\n\t\tT \u0026 v,\n\t\trapidjson::Value \u0026 to,\n\t\trapidjson::MemoryPoolAllocator\u003c\u003e \u0026 allocator ) const\n\t{\n\t\tusing json_dto::write_json_value;\n\t\tusing json_dto::string_ref_t;\n\n\t\tif( std::isnan(v) )\n\t\t\twrite_json_value( string_ref_t{\"nan\"}, to, allocator );\n\t\telse if( v \u003e std::numeric_limits\u003cT\u003e::max() )\n\t\t\twrite_json_value( string_ref_t{\"inf\"}, to, allocator );\n\t\telse if( v \u003c std::numeric_limits\u003cT\u003e::min() )\n\t\t\twrite_json_value( string_ref_t{\"-inf\"}, to, allocator );\n\t\telse\n\t\t\twrite_json_value( v, to, allocator );\n\t}\n};\n\nstruct struct_with_floats_t\n{\n\tfloat m_num_float;\n\tdouble m_num_double;\n\n\ttemplate\u003c typename Json_Io \u003e\n\tvoid\n\tjson_io( Json_Io \u0026 io )\n\t{\n\t\tio\n\t\t\t\u0026 optional( custom_floating_point_reader_writer{},\n\t\t\t\t\t\"num_float\", m_num_float, 0.0f )\n\t\t\t\u0026 optional( custom_floating_point_reader_writer{},\n\t\t\t\t\t\"num_double\", m_num_double, 0.0 );\n\t}\n};\n```\n\nNote also that `read` and `write` methods of Reader_Writer class can be template methods.\n\nA custom Reader_Writer can also be used to change representation of a field. For example, let suppose that we have a `std::vector\u003csome_struct\u003e` field, but this field has to be represented as a single object if it holds just one value, and as an array otherwise. Something like:\n\n```\n{\n  \"message\": {\n    \"from\": \"address-1\",\n    \"to\": \"address-2\",\n    ...,\n    \"extension\": {...}\n  },\n  ...\n}\n```\n\nif we have only one extension in a message or:\n\n```\n{\n  \"message\": {\n    \"from\": \"address-1\",\n    \"to\": \"address-2\",\n    ...,\n    \"extension\": [\n      {...},\n      {...},\n      ...\n    ]\n  },\n  ...\n}\n```\n\nif there are several extensions.\n\nA solution with a custom Reader_Writer can looks like:\n\n```cpp\nstruct extension\n{\n    ...\n\n    template\u003c typename Json_Io \u003e\n    void json_io( Json_Io \u0026 io )\n    {\n        ... // Ordinary serialization/deserialization code.\n    }\n};\n\n// Reader_Writer for vector of `extension` objects.\nstruct extension_reader_writer\n{\n    void read( std::vector\u003c extension \u003e \u0026 to, const rapidjson::Value \u0026 from ) const\n    {\n        using json_dto::read_json_value;\n\n        to.clear();\n\n        if( from.IsObject() )\n        {\n            extension_t single_value;\n            read_json_value( single_value, from );\n            to.push_back( std::move(single_value) );\n        }\n        else if( from.IsArray() )\n        {\n            read_json_value( to, from );\n        }\n        else\n        {\n            throw std::runtime_error{ \"Unexpected format of extension value\" };\n        }\n    }\n\n    void write(\n        const std::vector\u003c extension \u003e \u0026 v,\n        rapidjson::Value \u0026 to,\n        rapidjson::MemoryPoolAllocator\u003c\u003e \u0026 allocator ) const\n    {\n        using json_dto::write_json_value;\n        if( 1u == v.size() )\n            write_json_value( v.front(), to, allocator );\n        else\n            write_json_value( v, to, allocator );\n    }\n};\n\n// Message.\nstruct message\n{\n    // Fields of a message.\n    ...\n    // Extension(s) for a message.\n    std::vector\u003c extension \u003e m_extension;\n\n    template\u003c typename Json_Io \u003e\n    void json_io( Json_Io \u0026 io )\n    {\n        io \u0026 ...\n            \u0026 json_dto::mandatory( extension_reader_writer{},\n                    \"extension\", m_extension )\n            ;\n    }\n};\n```\n\nThe full example of such an approach can be seen [here](./dev/sample/tutorial18.1/main.cpp)\n\n#### Custom Reader_Writer with containers and nullable_t, and std::optional\n\nIf a custom Reader_Writer is used then a reference to the whole field is passed to Reader_Writer's methods. For example:\n\n```cpp\nstruct my_int_reader_writer\n{\n\tvoid read(int \u0026 v, ...) const {...} // Custom read procedure for an int.\n\n\tvoid write(const int \u0026 v, ...) const {...} // Custom write procedure for int.\n};\n...\nstruct my_data\n{\n\tint field_;\n\t...\n\ttemplate\u003ctypename Io\u003e void json_io(Io \u0026 io)\n\t{\n\t\tio \u0026 json_dto::mandatory(my_int_reader_writer{},\n\t\t\t\t\"field\", field_)\n\t\t\t...\n\t\t\t;\n\t}\n};\n```\n\nIn that case a reference to an `int` will be passed to `my_int_reader_writer`'s `read` and `write` methods.\n\nIn the case when `my_data` isn't `int` but a `std::vector\u003cint\u003e` then a reference to `std::vector\u003cint\u003e` instance will be passed to `read`/`write`. And there will be a compiler error because `read`/`write` expects a reference to an `int`.\n\nIf we want our custom Reader_Writer to be applied for every member of a container then `json_dto::apply_to_content_t` proxy should be used as Reader_Writer type:\n\n```cpp\nstruct my_complex_data\n{\n\tstd::vector\u003cint\u003e field_;\n\t...\n\ttemplate\u003ctypename Io\u003e void json_io(Io \u0026 io)\n\t{\n\t\tio \u0026 json_dto::mandatory(\n\t\t\t\tjson_dto::apply_to_content_t\u003cmy_int_reader_writer\u003e{},\n\t\t\t\t\"field\", field_)\n\t\t\t...\n\t\t\t;\n\t}\n};\n```\n\nThe `apply_to_content_t` proxy works very simple way: it holds an instance of an actual Reader_Writer and applies that actual Reader_Writer to every member of a container (or to the content of `json_dto::nullable_t` and `std::optional`, see bellow).\n\nThe same rule is applied to `nullable_t` and `std::optional`:\n\n```cpp\nstruct my_data\n{\n\tstd::optional\u003cint\u003e field_;\n\t...\n\ttemplate\u003ctypename Io\u003e void json_io(Io \u0026 io)\n\t{\n\t\tio \u0026 json_dto::optional(my_int_reader_writer{},\n\t\t\t\t\"field\", field_, std::nullopt)\n\t\t\t...\n\t\t\t;\n\t}\n};\n```\n\nSuch code leads to compiler error because `my_int_reader_writer`'s `read` and `write` methods expect a reference to `int`, not to `std::optional\u003cint\u003e`. So we have to use `apply_to_content_t` here too:\n\n```cpp\nstruct my_data\n{\n\tstd::optional\u003cint\u003e field_;\n\t...\n\ttemplate\u003ctypename Io\u003e void json_io(Io \u0026 io)\n\t{\n\t\tio \u0026 json_dto::optional(\n\t\t\t\t// Now my_int_reader_writer will be applied to the content\n\t\t\t\t// of std::optional\u003cint\u003e, not to std::optional\u003cint\u003e itself.\n\t\t\t\tjson_dto::apply_to_content_t\u003cmy_int_reader_writer\u003e{},\n\t\t\t\t\"field\", field_, std::nullopt)\n\t\t\t...\n\t\t\t;\n\t}\n};\n```\n\nNote that `apply_to_content_t` can be nested:\n\n```cpp\nstruct my_complex_data {\n\tjson_dto::nullable_t\u003c std::vector\u003cint\u003e \u003e params_;\n\t...\n\ttemplate\u003ctypename Io\u003e void json_io(Io \u0026 io) {\n\t\tio \u0026 json_dto::mandatory(\n\t\t\t\t// The first occurence of apply_to_content_t is for nullable_t.\n\t\t\t\tjson_dto::apply_to_content_t\u003c\n\t\t\t\t\t// The second occurence of apply_to_content_t is for std::vector.\n\t\t\t\t\tjson_dto::apply_to_content_t\u003c\n\t\t\t\t\t\t// This is for the content of std::vector.\n\t\t\t\t\t\tmy_int_reader_writer\n\t\t\t\t\t\u003e\n\t\t\t\t\u003e{},\n\t\t\t\t\"params\", params_)\n\t\t\t...\n\t\t\t;\n\t}\n};\n```\n\n### Overloading of read_json_value/write_json_value for const_map_key_t/mutable_map_key_t\n\nSince v.0.2.11 json_dto (de)serializes keys of map-like containers (`std::map`, `std::multimap`, `std::unordered_map` and so on) by using new proxy types `const_map_key_t` and `mutable_map_key_t`.\n\nA new type `const_map_key_t\u003cT\u003e` is used for serializing a key of type T.\n\nA new type `mutable_map_key_t\u003cT\u003e` is used for deserializing a key of type T.\n\nIt means that if someone wants to make overloads of `read_json_value` and `write_json_value` for types that are used as keys in map-like structures, then such overloads should be placed into `json_dto` namespace and should have the following prototypes:\n\n```cpp\nnamespace json_dto {\n\nvoid read_json_value(\n\tmutable_map_key_t\u003cUserType\u003e key,\n\tconst rapidjson::Value \u0026 from);\n\nvoid write_json_value(\n\tconst_map_key_t\u003cUserType\u003e key,\n\trapidjson::Value \u0026 to,\n\trapidjson::MemoryPoolAllocator\u003c\u003e \u0026 allocator);\n\n} /* namespace json_dto */\n```\n\n[See full example with overloading of read/write_json_value for mutable/const_map_key_t](./dev/sample/tutorial19/main.cpp)\n\n### Custom Reader_Writer and mutable/const_map_key_t\n\nThe addition of `mutable_map_key_t`/`const_map_key_t` in the v.0.2.11 means that custom Reader_Writers should take the presence of those types into the account.\n\nFor example, if a custom Reader_Writer is used for (de)serializing a content of `std::map` then that Reader_Writer should have implementations of `read`/`write` methods for keys and values from the map:\n\n```cpp\nstruct my_kv_formatter\n{\n\t// Read a key.\n\tvoid read(\n\t\tjson_dto::mutable_map_key_t\u003cKeyType\u003e \u0026 key,\n\t\tconst rapidjson::Value \u0026 from) const {...}\n\n\t// Read a value.\n\tvoid read(\n\t\tValueType \u0026 value,\n\t\tconst rapidjson::Value \u0026 from) const {...}\n\n\t// Write a key.\n\tvoid write(\n\t\tconst json_dto::const_map_key_t\u003cKeyType\u003e \u0026 key,\n\t\trapidjson::Value \u0026 to,\n\t\trapidjson::MemoryPoolAllocator\u003c\u003e \u0026 allocator) const {...}\n\n\t// Write a value.\n\tvoid write(\n\t\tconst ValueType \u0026 value,\n\t\trapidjson::Value \u0026 to,\n\t\trapidjson::MemoryPoolAllocator\u003c\u003e \u0026 allocator) const {...}\n};\n```\n\nPlease note that a references to instances of `mutable_map_key_t`/`const_map_key_t` are passed to `read`/`write` methods.\n\n[See full example with overloading of Reader_Writer for mutable/const_map_key_t](./dev/sample/tutorial19.1/main.cpp)\n\n# License\n\n*json_dto* is distributed under\n[BSD-3-Clause](http://spdx.org/licenses/BSD-3-Clause.html) license. See LICENSE\nfile for more information.\n\nFor the license of *rapidson* library see LICENSE file in *rapidson*\ndistributive.\n\nFor the license of *rapidson_mxxru* library see LICENSE file in *rapidson_mxxru*\ndistributive.\n\nFor the license of *CATCH* library see LICENSE file in *CATCH*\ndistributive.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstiffstream%2Fjson_dto","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstiffstream%2Fjson_dto","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstiffstream%2Fjson_dto/lists"}