🔀 merge pull request #435 (based on #338, #355, and #423)

conversion from/to user-defined types
This commit is contained in:
Niels Lohmann 2017-01-27 06:22:10 +01:00 committed by GitHub
commit 42fa3f0568
54 changed files with 3634 additions and 2018 deletions

231
README.md
View file

@ -21,6 +21,7 @@
- [Conversion from STL containers](#conversion-from-stl-containers)
- [JSON Pointer and JSON Patch](#json-pointer-and-json-patch)
- [Implicit conversions](#implicit-conversions)
- [Conversions to/from arbitrary types](#arbitrary-types-conversions)
- [Binary formats (CBOR and MessagePack)](#binary-formats-cbor-and-messagepack)
- [Supported compilers](#supported-compilers)
- [License](#license)
@ -442,6 +443,224 @@ int vi = jn.get<int>();
// etc.
```
### Arbitrary types conversions
Every type can be serialized in JSON, not just STL-containers and scalar types. Usually, you would do something along those lines:
```cpp
namespace ns {
// a simple struct to model a person
struct person {
std::string name;
std::string address;
int age;
};
}
ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};
// convert to JSON: copy each value into the JSON object
json j;
j["name"] = p.name;
j["address"] = p.address;
j["age"] = p.age;
// ...
// convert from JSON: copy each value from the JSON object
ns::person p {
j["name"].get<std::string>(),
j["address"].get<std::string>(),
j["age"].get<int>()
};
```
It works, but that's quite a lot of boilerplate... Fortunately, there's a better way:
```cpp
// create a person
ns::person p {"Ned Flanders", "744 Evergreen Terrace", 60};
// conversion: person -> json
json j = p;
std::cout << j << std::endl;
// {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"}
// conversion: json -> person
ns::person p2 = j;
// that's it
assert(p == p2);
```
#### Basic usage
To make this work with one of your types, you only need to provide two functions:
```cpp
using nlohmann::json;
namespace ns {
void to_json(json& j, const person& p) {
j = json{{"name", p.name}, {"address", p.address}, {"age", p.age}};
}
void from_json(const json& j, person& p) {
p.name = j["name"].get<std::string>();
p.address = j["address"].get<std::string>();
p.age = j["age"].get<int>();
}
} // namespace ns
```
That's all! When calling the `json` constructor with your type, your custom `to_json` method will be automatically called.
Likewise, when calling `get<your_type>()`, the `from_json` method will be called.
Some important things:
* Those methods **MUST** be in your type's namespace (which can be the global namespace), or the library will not be able to locate them (in this example, they are in namespace `ns`, where `person` is defined).
* When using `get<your_type>()`, `your_type` **MUST** be [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). (There is a way to bypass this requirement described later.)
#### How do I convert third-party types?
This requires a bit more advanced technique. But first, let's see how this conversion mechanism works:
The library uses **JSON Serializers** to convert types to json.
The default serializer for `nlohmann::json` is `nlohmann::adl_serializer` (ADL means [Argument-Dependent Lookup](http://en.cppreference.com/w/cpp/language/adl)).
It is implemented like this (simplified):
```cpp
template <typename T>
struct adl_serializer {
static void to_json(json& j, const T& value) {
// calls the "to_json" method in T's namespace
}
static void from_json(const json& j, T& value) {
// same thing, but with the "from_json" method
}
};
```
This serializer works fine when you have control over the type's namespace. However, what about `boost::optional`, or `std::filesystem::path` (C++17)? Hijacking the `boost` namespace is pretty bad, and it's illegal to add something other than template specializations to `std`...
To solve this, you need to add a specialization of `adl_serializer` to the `nlohmann` namespace, here's an example:
```cpp
// partial specialization (full specialization works too)
namespace nlohmann {
template <typename T>
struct adl_serializer<boost::optional<T>> {
static void to_json(json& j, const boost::optional<T>& opt) {
if (opt == boost::none) {
j = nullptr;
} else {
j = *opt; // this will call adl_serializer<T>::to_json which will
// find the free function to_json in T's namespace!
}
}
static void from_json(const json& j, boost::optional<T>& opt) {
if (!j.is_null()) {
opt = j.get<T>(); // same as above, but with
// adl_serializer<T>::from_json
}
}
};
}
```
#### How can I use `get()` for non-default constructible/non-copyable types?
There is a way, if your type is [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible). You will need to specialize the `adl_serializer` as well, but with a special `from_json` overload:
```cpp
struct move_only_type {
move_only_type() = delete;
move_only_type(int ii): i(ii) {}
move_only_type(const move_only_type&) = delete;
move_only_type(move_only_type&&) = default;
int i;
};
namespace nlohmann {
template <>
struct adl_serializer<move_only_type> {
// note: the return type is no longer 'void', and the method only takes
// one argument
static move_only_type from_json(const json& j) {
return {j.get<int>()};
}
// Here's the catch! You must provide a to_json method! Otherwise you
// will not be able to convert move_only_type to json, since you fully
// specialized adl_serializer on that type
static void to_json(json& j, move_only_type t) {
j = t.i;
}
};
}
```
#### Can I write my own serializer? (Advanced use)
Yes. You might want to take a look at [`unit-udt.cpp`](https://github.com/nlohmann/json/blob/develop/test/src/unit-udt.cpp) in the test suite, to see a few examples.
If you write your own serializer, you'll need to do a few things:
* use a different `basic_json` alias than `nlohmann::json` (the last template parameter of `basic_json` is the `JSONSerializer`)
* use your `basic_json` alias (or a template parameter) in all your `to_json`/`from_json` methods
* use `nlohmann::to_json` and `nlohmann::from_json` when you need ADL
Here is an example, without simplifications, that only accepts types with a size <= 32, and uses ADL.
```cpp
// You should use void as a second template argument
// if you don't need compile-time checks on T
template<typename T, typename SFINAE = typename std::enable_if<sizeof(T) <= 32>::type>
struct less_than_32_serializer {
template <typename BasicJsonType>
static void to_json(BasicJsonType& j, T value) {
// we want to use ADL, and call the correct to_json overload
using nlohmann::to_json; // this method is called by adl_serializer,
// this is where the magic happens
to_json(j, value);
}
template <typename BasicJsonType>
static void from_json(const BasicJsonType& j, T& value) {
// same thing here
using nlohmann::from_json;
from_json(j, value);
}
};
```
Be **very** careful when reimplementing your serializer, you can stack overflow if you don't pay attention:
```cpp
template <typename T, void>
struct bad_serializer
{
template <typename BasicJsonType>
static void to_json(BasicJsonType& j, const T& value) {
// this calls BasicJsonType::json_serializer<T>::to_json(j, value);
// if BasicJsonType::json_serializer == bad_serializer ... oops!
j = value;
}
template <typename BasicJsonType>
static void to_json(const BasicJsonType& j, T& value) {
// this calls BasicJsonType::json_serializer<T>::from_json(j, value);
// if BasicJsonType::json_serializer == bad_serializer ... oops!
value = j.template get<T>(); // oops!
}
};
```
### Binary formats (CBOR and MessagePack)
Though JSON is a ubiquitous data format, it is not a very compact format suitable for data exchange, for instance over a network. Hence, the library supports [CBOR](http://cbor.io) (Concise Binary Object Representation) and [MessagePack](http://msgpack.org) to efficiently encode JSON values to byte vectors and to decode such vectors.
@ -546,7 +765,7 @@ I deeply appreciate the help of the following people.
- [Eric Cornelius](https://github.com/EricMCornelius) pointed out a bug in the handling with NaN and infinity values. He also improved the performance of the string escaping.
- [易思龙](https://github.com/likebeta) implemented a conversion from anonymous enums.
- [kepkin](https://github.com/kepkin) patiently pushed forward the support for Microsoft Visual studio.
- [gregmarr](https://github.com/gregmarr) simplified the implementation of reverse iterators and helped with numerous hints and improvements.
- [gregmarr](https://github.com/gregmarr) simplified the implementation of reverse iterators and helped with numerous hints and improvements. In particular, he pushed forward the implementation of user-defined types.
- [Caio Luppi](https://github.com/caiovlp) fixed a bug in the Unicode handling.
- [dariomt](https://github.com/dariomt) fixed some typos in the examples.
- [Daniel Frey](https://github.com/d-frey) cleaned up some pointers and implemented exception-safe memory allocation.
@ -574,7 +793,7 @@ I deeply appreciate the help of the following people.
- [duncanwerner](https://github.com/duncanwerner) found a really embarrassing performance regression in the 2.0.0 release.
- [Damien](https://github.com/dtoma) fixed one of the last conversion warnings.
- [Thomas Braun](https://github.com/t-b) fixed a warning in a test case.
- [Théo DELRIEU](https://github.com/theodelrieu) patiently and constructively oversaw the long way toward [iterator-range parsing](https://github.com/nlohmann/json/issues/290).
- [Théo DELRIEU](https://github.com/theodelrieu) patiently and constructively oversaw the long way toward [iterator-range parsing](https://github.com/nlohmann/json/issues/290). He also implemented the magic behind the serialization/deserialization of user-defined types.
- [Stefan](https://github.com/5tefan) fixed a minor issue in the documentation.
- [Vasil Dimov](https://github.com/vasild) fixed the documentation regarding conversions from `std::multiset`.
- [ChristophJud](https://github.com/ChristophJud) overworked the CMake files to ease project inclusion.
@ -588,6 +807,9 @@ I deeply appreciate the help of the following people.
- [Bosswestfalen](https://github.com/Bosswestfalen) merged two iterator classes into a smaller one.
- [Daniel599](https://github.com/Daniel599) helped to get Travis execute the tests with Clang's sanitizers.
- [Jonathan Lee](https://github.com/vjon) fixed an example in the README file.
- [gnzlbg](https://github.com/gnzlbg) supported the implementation of user-defined types.
- [Alexej Harm](https://github.com/qis) helped to get the user-defined types working with Visual Studio.
- [Jared Grubb](https://github.com/jaredgrubb) supported the implementation of user-defined types.
Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone.
@ -611,10 +833,11 @@ Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I
To compile and run the tests, you need to execute
```sh
$ make check
$ make json_unit -Ctest
$ ./test/json_unit "*""
===============================================================================
All tests passed (11202040 assertions in 44 test cases)
All tests passed (11202052 assertions in 47 test cases)
```
Alternatively, you can use [CMake](https://cmake.org) and run

View file

@ -109,7 +109,7 @@ RECURSIVE = NO
EXCLUDE =
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS =
EXCLUDE_SYMBOLS = nlohmann::anonymous_namespace
EXCLUDE_SYMBOLS = nlohmann::detail
EXAMPLE_PATH = examples
EXAMPLE_PATTERNS =
EXAMPLE_RECURSIVE = NO

View file

@ -1,58 +0,0 @@
#include <json.hpp>
#include <deque>
#include <list>
#include <forward_list>
#include <set>
#include <unordered_set>
using json = nlohmann::json;
int main()
{
// create an array from std::vector
std::vector<int> c_vector {1, 2, 3, 4};
json j_vec(c_vector);
// create an array from std::deque
std::deque<double> c_deque {1.2, 2.3, 3.4, 5.6};
json j_deque(c_deque);
// create an array from std::list
std::list<bool> c_list {true, true, false, true};
json j_list(c_list);
// create an array from std::forward_list
std::forward_list<int64_t> c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543};
json j_flist(c_flist);
// create an array from std::array
std::array<unsigned long, 4> c_array {{1, 2, 3, 4}};
json j_array(c_array);
// create an array from std::set
std::set<std::string> c_set {"one", "two", "three", "four", "one"};
json j_set(c_set); // only one entry for "one" is used
// create an array from std::unordered_set
std::unordered_set<std::string> c_uset {"one", "two", "three", "four", "one"};
json j_uset(c_uset); // only one entry for "one" is used
// create an array from std::multiset
std::multiset<std::string> c_mset {"one", "two", "one", "four"};
json j_mset(c_mset); // both entries for "one" are used
// create an array from std::unordered_multiset
std::unordered_multiset<std::string> c_umset {"one", "two", "one", "four"};
json j_umset(c_umset); // both entries for "one" are used
// serialize the JSON arrays
std::cout << j_vec << '\n';
std::cout << j_deque << '\n';
std::cout << j_list << '\n';
std::cout << j_flist << '\n';
std::cout << j_array << '\n';
std::cout << j_set << '\n';
std::cout << j_uset << '\n';
std::cout << j_mset << '\n';
std::cout << j_umset << '\n';
}

View file

@ -1 +0,0 @@
<a target="_blank" href="http://melpon.org/wandbox/permlink/3BIhBw91FUVuHE1D"><b>online</b></a>

View file

@ -1,9 +0,0 @@
[1,2,3,4]
[1.2,2.3,3.4,5.6]
[true,true,false,true]
[12345678909876,23456789098765,34567890987654,45678909876543]
[1,2,3,4]
["four","one","three","two"]
["four","three","two","one"]
["four","one","one","two"]
["four","two","one","one"]

View file

@ -1,27 +0,0 @@
#include <json.hpp>
using json = nlohmann::json;
int main()
{
// create values of different integer types
short n42 = 42;
int n23 = 23;
long n1024 = 1024;
int_least32_t n17 = 17;
uint8_t n8 = 8;
// create JSON numbers
json j42(n42);
json j23(n23);
json j1024(n1024);
json j17(n17);
json j8(n8);
// serialize the JSON numbers
std::cout << j42 << '\n';
std::cout << j23 << '\n';
std::cout << j1024 << '\n';
std::cout << j17 << '\n';
std::cout << j8 << '\n';
}

View file

@ -1 +0,0 @@
<a target="_blank" href="http://melpon.org/wandbox/permlink/PcMzhcu2RpD7KSwr"><b>online</b></a>

View file

@ -1,5 +0,0 @@
42
23
1024
17
8

View file

@ -1,21 +0,0 @@
#include <json.hpp>
using json = nlohmann::json;
int main()
{
// create values of different floating-point types
float f42 = 42.23;
float f_nan = 1.0f / 0.0f;
double f23 = 23.42;
// create JSON numbers
json j42(f42);
json j_nan(f_nan);
json j23(f23);
// serialize the JSON numbers
std::cout << j42 << '\n';
std::cout << j_nan << '\n';
std::cout << j23 << '\n';
}

View file

@ -1 +0,0 @@
<a target="_blank" href="http://melpon.org/wandbox/permlink/2TCYWSrOxnR05AZI"><b>online</b></a>

View file

@ -1,3 +0,0 @@
42.2299995422363
null
23.42

View file

@ -1,41 +0,0 @@
#include <json.hpp>
#include <unordered_map>
using json = nlohmann::json;
int main()
{
// create an object from std::map
std::map<std::string, int> c_map
{
{"one", 1}, {"two", 2}, {"three", 3}
};
json j_map(c_map);
// create an object from std::unordered_map
std::unordered_map<const char*, double> c_umap
{
{"one", 1.2}, {"two", 2.3}, {"three", 3.4}
};
json j_umap(c_umap);
// create an object from std::multimap
std::multimap<std::string, bool> c_mmap
{
{"one", true}, {"two", true}, {"three", false}, {"three", true}
};
json j_mmap(c_mmap); // only one entry for key "three" is used
// create an object from std::unordered_multimap
std::unordered_multimap<std::string, bool> c_ummap
{
{"one", true}, {"two", true}, {"three", false}, {"three", true}
};
json j_ummap(c_ummap); // only one entry for key "three" is used
// serialize the JSON objects
std::cout << j_map << '\n';
std::cout << j_umap << '\n';
std::cout << j_mmap << '\n';
std::cout << j_ummap << '\n';
}

View file

@ -1 +0,0 @@
<a target="_blank" href="http://melpon.org/wandbox/permlink/JzLCMcSXNsh4uVWa"><b>online</b></a>

View file

@ -1,4 +0,0 @@
{"one":1,"three":3,"two":2}
{"one":1.2,"three":3.4,"two":2.3}
{"one":true,"three":false,"two":true}
{"one":true,"three":false,"two":true}

View file

@ -1,15 +0,0 @@
#include <json.hpp>
using json = nlohmann::json;
int main()
{
// create a string value
std::string s = "The quick brown fox jumps over the lazy dog.";
// create a JSON string value
json j = s;
// serialize the JSON string
std::cout << j << '\n';
}

View file

@ -1 +0,0 @@
<a target="_blank" href="http://melpon.org/wandbox/permlink/b9hbCY8zfOiTOuoK"><b>online</b></a>

View file

@ -1 +0,0 @@
"The quick brown fox jumps over the lazy dog."

View file

@ -0,0 +1,211 @@
#include <json.hpp>
#include <deque>
#include <list>
#include <forward_list>
#include <set>
#include <unordered_map>
#include <unordered_set>
using json = nlohmann::json;
int main()
{
// ============
// object types
// ============
// create an object from an object_t value
json::object_t object_value = { {"one", 1}, {"two", 2} };
json j_object_t(object_value);
// create an object from std::map
std::map<std::string, int> c_map
{
{"one", 1}, {"two", 2}, {"three", 3}
};
json j_map(c_map);
// create an object from std::unordered_map
std::unordered_map<const char*, double> c_umap
{
{"one", 1.2}, {"two", 2.3}, {"three", 3.4}
};
json j_umap(c_umap);
// create an object from std::multimap
std::multimap<std::string, bool> c_mmap
{
{"one", true}, {"two", true}, {"three", false}, {"three", true}
};
json j_mmap(c_mmap); // only one entry for key "three" is used
// create an object from std::unordered_multimap
std::unordered_multimap<std::string, bool> c_ummap
{
{"one", true}, {"two", true}, {"three", false}, {"three", true}
};
json j_ummap(c_ummap); // only one entry for key "three" is used
// serialize the JSON objects
std::cout << j_object_t << '\n';
std::cout << j_map << '\n';
std::cout << j_umap << '\n';
std::cout << j_mmap << '\n';
std::cout << j_ummap << "\n\n";
// ===========
// array types
// ===========
// create an array from an array_t value
json::array_t array_value = {"one", "two", 3, 4.5, false};
json j_array_t(array_value);
// create an array from std::vector
std::vector<int> c_vector {1, 2, 3, 4};
json j_vec(c_vector);
// create an array from std::deque
std::deque<double> c_deque {1.2, 2.3, 3.4, 5.6};
json j_deque(c_deque);
// create an array from std::list
std::list<bool> c_list {true, true, false, true};
json j_list(c_list);
// create an array from std::forward_list
std::forward_list<int64_t> c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543};
json j_flist(c_flist);
// create an array from std::array
std::array<unsigned long, 4> c_array {{1, 2, 3, 4}};
json j_array(c_array);
// create an array from std::set
std::set<std::string> c_set {"one", "two", "three", "four", "one"};
json j_set(c_set); // only one entry for "one" is used
// create an array from std::unordered_set
std::unordered_set<std::string> c_uset {"one", "two", "three", "four", "one"};
json j_uset(c_uset); // only one entry for "one" is used
// create an array from std::multiset
std::multiset<std::string> c_mset {"one", "two", "one", "four"};
json j_mset(c_mset); // both entries for "one" are used
// create an array from std::unordered_multiset
std::unordered_multiset<std::string> c_umset {"one", "two", "one", "four"};
json j_umset(c_umset); // both entries for "one" are used
// serialize the JSON arrays
std::cout << j_array_t << '\n';
std::cout << j_vec << '\n';
std::cout << j_deque << '\n';
std::cout << j_list << '\n';
std::cout << j_flist << '\n';
std::cout << j_array << '\n';
std::cout << j_set << '\n';
std::cout << j_uset << '\n';
std::cout << j_mset << '\n';
std::cout << j_umset << "\n\n";
// ============
// string types
// ============
// create string from a string_t value
json::string_t string_value = "The quick brown fox jumps over the lazy dog.";
json j_string_t(string_value);
// create a JSON string directly from a string literal
json j_string_literal("The quick brown fox jumps over the lazy dog.");
// create string from std::string
std::string s_stdstring = "The quick brown fox jumps over the lazy dog.";
json j_stdstring(s_stdstring);
// serialize the JSON strings
std::cout << j_string_t << '\n';
std::cout << j_string_literal << '\n';
std::cout << j_stdstring << "\n\n";
// ============
// number types
// ============
// create a JSON number from number_integer_t
json::number_integer_t value_integer_t = -42;
json j_integer_t(value_integer_t);
// create a JSON number from number_unsigned_t
json::number_integer_t value_unsigned_t = 17;
json j_unsigned_t(value_unsigned_t);
// create a JSON number from an anonymous enum
enum { enum_value = 17 };
json j_enum(enum_value);
// create values of different integer types
short n_short = 42;
int n_int = -23;
long n_long = 1024;
int_least32_t n_int_least32_t = -17;
uint8_t n_uint8_t = 8;
// create (integer) JSON numbers
json j_short(n_short);
json j_int(n_int);
json j_long(n_long);
json j_int_least32_t(n_int_least32_t);
json j_uint8_t(n_uint8_t);
// create values of different floating-point types
json::number_float_t v_ok = 3.141592653589793;
json::number_float_t v_nan = NAN;
json::number_float_t v_infinity = INFINITY;
// create values of different floating-point types
float n_float = 42.23;
float n_float_nan = 1.0f / 0.0f;
double n_double = 23.42;
// create (floating point) JSON numbers
json j_ok(v_ok);
json j_nan(v_nan);
json j_infinity(v_infinity);
json j_float(n_float);
json j_float_nan(n_float_nan);
json j_double(n_double);
// serialize the JSON numbers
std::cout << j_integer_t << '\n';
std::cout << j_unsigned_t << '\n';
std::cout << j_enum << '\n';
std::cout << j_short << '\n';
std::cout << j_int << '\n';
std::cout << j_long << '\n';
std::cout << j_int_least32_t << '\n';
std::cout << j_uint8_t << '\n';
std::cout << j_ok << '\n';
std::cout << j_nan << '\n';
std::cout << j_infinity << '\n';
std::cout << j_float << '\n';
std::cout << j_float_nan << '\n';
std::cout << j_double << "\n\n";
// =============
// boolean types
// =============
// create boolean values
json j_truth = true;
json j_falsity = false;
// serialize the JSON booleans
std::cout << j_truth << '\n';
std::cout << j_falsity << '\n';
}

View file

@ -0,0 +1 @@
<a target="_blank" href="http://melpon.org/wandbox/permlink/VM7W2kpE7sIYJ5DW"><b>online</b></a>

View file

@ -0,0 +1,38 @@
{"one":1,"two":2}
{"one":1,"three":3,"two":2}
{"one":1.2,"three":3.4,"two":2.3}
{"one":true,"three":false,"two":true}
{"one":true,"three":false,"two":true}
["one","two",3,4.5,false]
[1,2,3,4]
[1.2,2.3,3.4,5.6]
[true,true,false,true]
[12345678909876,23456789098765,34567890987654,45678909876543]
[1,2,3,4]
["four","one","three","two"]
["four","three","two","one"]
["four","one","one","two"]
["four","two","one","one"]
"The quick brown fox jumps over the lazy dog."
"The quick brown fox jumps over the lazy dog."
"The quick brown fox jumps over the lazy dog."
-42
17
17
42
-23
1024
-17
8
3.14159265358979
null
null
42.2299995422363
null
23.42
true
false

View file

@ -1,15 +0,0 @@
#include <json.hpp>
using json = nlohmann::json;
int main()
{
// create an array_t value
json::array_t value = {"one", "two", 3, 4.5, false};
// create a JSON array from the value
json j(value);
// serialize the JSON array
std::cout << j << '\n';
}

View file

@ -1 +0,0 @@
<a target="_blank" href="http://melpon.org/wandbox/permlink/dTbSNAvl6TqrMEAn"><b>online</b></a>

View file

@ -1 +0,0 @@
["one","two",3,4.5,false]

View file

@ -1,14 +0,0 @@
#include <json.hpp>
using json = nlohmann::json;
int main()
{
// create boolean values
json j_truth = true;
json j_falsity = false;
// serialize the JSON booleans
std::cout << j_truth << '\n';
std::cout << j_falsity << '\n';
}

View file

@ -1 +0,0 @@
<a target="_blank" href="http://melpon.org/wandbox/permlink/VmVl9pyrQp8LyOnU"><b>online</b></a>

View file

@ -1,2 +0,0 @@
true
false

View file

@ -1,15 +0,0 @@
#include <json.hpp>
using json = nlohmann::json;
int main()
{
// an anonymous enum
enum { t = 17 };
// create a JSON number from the enum
json j(t);
// serialize the JSON numbers
std::cout << j << '\n';
}

View file

@ -1 +0,0 @@
<a target="_blank" href="http://melpon.org/wandbox/permlink/3xQ1qy7BT9OrSSCo"><b>online</b></a>

View file

@ -1 +0,0 @@
17

View file

@ -1,21 +0,0 @@
#include <json.hpp>
using json = nlohmann::json;
int main()
{
// create values of different floating-point types
json::number_float_t v_ok = 3.141592653589793;
json::number_float_t v_nan = NAN;
json::number_float_t v_infinity = INFINITY;
// create JSON numbers
json j_ok(v_ok);
json j_nan(v_nan);
json j_infinity(v_infinity);
// serialize the JSON numbers
std::cout << j_ok << '\n';
std::cout << j_nan << '\n';
std::cout << j_infinity << '\n';
}

View file

@ -1 +0,0 @@
<a target="_blank" href="http://melpon.org/wandbox/permlink/OTgOxjIAKFvxpFdm"><b>online</b></a>

View file

@ -1,3 +0,0 @@
3.14159265358979
null
null

View file

@ -1,14 +0,0 @@
#include <json.hpp>
using json = nlohmann::json;
int main()
{
// create a JSON number from number_integer_t
json::number_integer_t value = 42;
json j(value);
// serialize the JSON numbers
std::cout << j << '\n';
}

View file

@ -1 +0,0 @@
<a target="_blank" href="http://melpon.org/wandbox/permlink/cCQRCvjXdRM9YpT5"><b>online</b></a>

View file

@ -1,15 +0,0 @@
#include <json.hpp>
using json = nlohmann::json;
int main()
{
// create an object_t value
json::object_t value = { {"one", 1}, {"two", 2} };
// create a JSON object from the value
json j(value);
// serialize the JSON object
std::cout << j << '\n';
}

View file

@ -1 +0,0 @@
<a target="_blank" href="http://melpon.org/wandbox/permlink/DgtHcz5L6JphTOGJ"><b>online</b></a>

View file

@ -1 +0,0 @@
{"one":1,"two":2}

View file

@ -1,15 +0,0 @@
#include <json.hpp>
using json = nlohmann::json;
int main()
{
// create an string_t value
json::string_t value = "The quick brown fox jumps over the lazy doc";
// create a JSON string from the value
json j(value);
// serialize the JSON array
std::cout << j << '\n';
}

View file

@ -1 +0,0 @@
<a target="_blank" href="http://melpon.org/wandbox/permlink/cwNYP1q2mT8CFLTk"><b>online</b></a>

View file

@ -1 +0,0 @@
"The quick brown fox jumps over the lazy doc"

View file

@ -1,12 +0,0 @@
#include <json.hpp>
using json = nlohmann::json;
int main()
{
// create a JSON string directly from a string literal
json j("The quick brown fox jumps over the lazy doc");
// serialize the JSON array
std::cout << j << '\n';
}

View file

@ -1 +0,0 @@
<a target="_blank" href="http://melpon.org/wandbox/permlink/AtV4zVErfLwkileg"><b>online</b></a>

View file

@ -1 +0,0 @@
"The quick brown fox jumps over the lazy doc"

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -29,12 +29,14 @@ add_executable(${JSON_UNITTEST_TARGET_NAME}
"src/unit-meta.cpp"
"src/unit-modifiers.cpp"
"src/unit-msgpack.cpp"
"src/unit-noexcept.cpp"
"src/unit-pointer_access.cpp"
"src/unit-readme.cpp"
"src/unit-reference_access.cpp"
"src/unit-regression.cpp"
"src/unit-serialization.cpp"
"src/unit-testsuites.cpp"
"src/unit-udt.cpp"
"src/unit-unicode.cpp"
)

View file

@ -91,7 +91,7 @@ TEST_CASE("const_iterator class")
json j(json::value_t::null);
json::const_iterator it(&j);
it.set_begin();
CHECK(it == j.cbegin());
CHECK((it == j.cbegin()));
}
SECTION("object")
@ -99,7 +99,7 @@ TEST_CASE("const_iterator class")
json j(json::value_t::object);
json::const_iterator it(&j);
it.set_begin();
CHECK(it == j.cbegin());
CHECK((it == j.cbegin()));
}
SECTION("array")
@ -107,7 +107,7 @@ TEST_CASE("const_iterator class")
json j(json::value_t::array);
json::const_iterator it(&j);
it.set_begin();
CHECK(it == j.cbegin());
CHECK((it == j.cbegin()));
}
}
@ -118,7 +118,7 @@ TEST_CASE("const_iterator class")
json j(json::value_t::null);
json::const_iterator it(&j);
it.set_end();
CHECK(it == j.cend());
CHECK((it == j.cend()));
}
SECTION("object")
@ -126,7 +126,7 @@ TEST_CASE("const_iterator class")
json j(json::value_t::object);
json::const_iterator it(&j);
it.set_end();
CHECK(it == j.cend());
CHECK((it == j.cend()));
}
SECTION("array")
@ -134,7 +134,7 @@ TEST_CASE("const_iterator class")
json j(json::value_t::array);
json::const_iterator it(&j);
it.set_end();
CHECK(it == j.cend());
CHECK((it == j.cend()));
}
}
}
@ -220,48 +220,48 @@ TEST_CASE("const_iterator class")
{
json j(json::value_t::null);
json::const_iterator it = j.cbegin();
CHECK(it.m_it.primitive_iterator == 1);
CHECK((it.m_it.primitive_iterator.m_it == 1));
it++;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1));
}
SECTION("number")
{
json j(17);
json::const_iterator it = j.cbegin();
CHECK(it.m_it.primitive_iterator == 0);
CHECK((it.m_it.primitive_iterator.m_it == 0));
it++;
CHECK(it.m_it.primitive_iterator == 1);
CHECK((it.m_it.primitive_iterator.m_it == 1));
it++;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1));
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::const_iterator it = j.cbegin();
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin());
CHECK((it.m_it.object_iterator == it.m_object->m_value.object->begin()));
it++;
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end());
CHECK((it.m_it.object_iterator == it.m_object->m_value.object->end()));
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::const_iterator it = j.cbegin();
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin());
CHECK((it.m_it.array_iterator == it.m_object->m_value.array->begin()));
it++;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
it++;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
it++;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
it++;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator == it.m_object->m_value.array->end()));
}
}
@ -271,48 +271,48 @@ TEST_CASE("const_iterator class")
{
json j(json::value_t::null);
json::const_iterator it = j.cbegin();
CHECK(it.m_it.primitive_iterator == 1);
CHECK((it.m_it.primitive_iterator.m_it == 1));
++it;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1));
}
SECTION("number")
{
json j(17);
json::const_iterator it = j.cbegin();
CHECK(it.m_it.primitive_iterator == 0);
CHECK((it.m_it.primitive_iterator.m_it == 0));
++it;
CHECK(it.m_it.primitive_iterator == 1);
CHECK((it.m_it.primitive_iterator.m_it == 1));
++it;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1));
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::const_iterator it = j.cbegin();
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin());
CHECK((it.m_it.object_iterator == it.m_object->m_value.object->begin()));
++it;
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end());
CHECK((it.m_it.object_iterator == it.m_object->m_value.object->end()));
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::const_iterator it = j.cbegin();
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin());
CHECK((it.m_it.array_iterator == it.m_object->m_value.array->begin()));
++it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
++it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
++it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
++it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator == it.m_object->m_value.array->end()));
}
}
@ -322,46 +322,46 @@ TEST_CASE("const_iterator class")
{
json j(json::value_t::null);
json::const_iterator it = j.cend();
CHECK(it.m_it.primitive_iterator == 1);
CHECK((it.m_it.primitive_iterator.m_it == 1));
}
SECTION("number")
{
json j(17);
json::const_iterator it = j.cend();
CHECK(it.m_it.primitive_iterator == 1);
CHECK((it.m_it.primitive_iterator.m_it == 1));
it--;
CHECK(it.m_it.primitive_iterator == 0);
CHECK((it.m_it.primitive_iterator.m_it == 0));
it--;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1));
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::const_iterator it = j.cend();
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end());
CHECK((it.m_it.object_iterator == it.m_object->m_value.object->end()));
it--;
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin());
CHECK((it.m_it.object_iterator == it.m_object->m_value.object->begin()));
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::const_iterator it = j.cend();
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator == it.m_object->m_value.array->end()));
it--;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
it--;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
it--;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
it--;
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator == it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
}
}
@ -371,46 +371,46 @@ TEST_CASE("const_iterator class")
{
json j(json::value_t::null);
json::const_iterator it = j.cend();
CHECK(it.m_it.primitive_iterator == 1);
CHECK((it.m_it.primitive_iterator.m_it == 1));
}
SECTION("number")
{
json j(17);
json::const_iterator it = j.cend();
CHECK(it.m_it.primitive_iterator == 1);
CHECK((it.m_it.primitive_iterator.m_it == 1));
--it;
CHECK(it.m_it.primitive_iterator == 0);
CHECK((it.m_it.primitive_iterator.m_it == 0));
--it;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1));
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::const_iterator it = j.cend();
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end());
CHECK((it.m_it.object_iterator == it.m_object->m_value.object->end()));
--it;
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin());
CHECK((it.m_it.object_iterator == it.m_object->m_value.object->begin()));
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::const_iterator it = j.cend();
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator == it.m_object->m_value.array->end()));
--it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
--it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
--it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
--it;
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator == it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
}
}
}

View file

@ -75,7 +75,7 @@ TEST_CASE("iterator class")
json j(json::value_t::null);
json::iterator it(&j);
it.set_begin();
CHECK(it == j.begin());
CHECK((it == j.begin()));
}
SECTION("object")
@ -83,7 +83,7 @@ TEST_CASE("iterator class")
json j(json::value_t::object);
json::iterator it(&j);
it.set_begin();
CHECK(it == j.begin());
CHECK((it == j.begin()));
}
SECTION("array")
@ -91,7 +91,7 @@ TEST_CASE("iterator class")
json j(json::value_t::array);
json::iterator it(&j);
it.set_begin();
CHECK(it == j.begin());
CHECK((it == j.begin()));
}
}
@ -102,7 +102,7 @@ TEST_CASE("iterator class")
json j(json::value_t::null);
json::iterator it(&j);
it.set_end();
CHECK(it == j.end());
CHECK((it == j.end()));
}
SECTION("object")
@ -110,7 +110,7 @@ TEST_CASE("iterator class")
json j(json::value_t::object);
json::iterator it(&j);
it.set_end();
CHECK(it == j.end());
CHECK((it == j.end()));
}
SECTION("array")
@ -118,7 +118,7 @@ TEST_CASE("iterator class")
json j(json::value_t::array);
json::iterator it(&j);
it.set_end();
CHECK(it == j.end());
CHECK((it == j.end()));
}
}
}
@ -204,48 +204,48 @@ TEST_CASE("iterator class")
{
json j(json::value_t::null);
json::iterator it = j.begin();
CHECK(it.m_it.primitive_iterator == 1);
CHECK((it.m_it.primitive_iterator.m_it == 1));
it++;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1));
}
SECTION("number")
{
json j(17);
json::iterator it = j.begin();
CHECK(it.m_it.primitive_iterator == 0);
CHECK((it.m_it.primitive_iterator.m_it == 0));
it++;
CHECK(it.m_it.primitive_iterator == 1);
CHECK((it.m_it.primitive_iterator.m_it == 1));
it++;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1));
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::iterator it = j.begin();
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin());
CHECK((it.m_it.object_iterator == it.m_object->m_value.object->begin()));
it++;
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end());
CHECK((it.m_it.object_iterator == it.m_object->m_value.object->end()));
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::iterator it = j.begin();
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin());
CHECK((it.m_it.array_iterator == it.m_object->m_value.array->begin()));
it++;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
it++;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
it++;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
it++;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator == it.m_object->m_value.array->end()));
}
}
@ -255,48 +255,48 @@ TEST_CASE("iterator class")
{
json j(json::value_t::null);
json::iterator it = j.begin();
CHECK(it.m_it.primitive_iterator == 1);
CHECK((it.m_it.primitive_iterator.m_it == 1));
++it;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1));
}
SECTION("number")
{
json j(17);
json::iterator it = j.begin();
CHECK(it.m_it.primitive_iterator == 0);
CHECK((it.m_it.primitive_iterator.m_it == 0));
++it;
CHECK(it.m_it.primitive_iterator == 1);
CHECK((it.m_it.primitive_iterator.m_it == 1));
++it;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1));
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::iterator it = j.begin();
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin());
CHECK((it.m_it.object_iterator == it.m_object->m_value.object->begin()));
++it;
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end());
CHECK((it.m_it.object_iterator == it.m_object->m_value.object->end()));
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::iterator it = j.begin();
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin());
CHECK((it.m_it.array_iterator == it.m_object->m_value.array->begin()));
++it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
++it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
++it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
++it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator == it.m_object->m_value.array->end()));
}
}
@ -306,46 +306,46 @@ TEST_CASE("iterator class")
{
json j(json::value_t::null);
json::iterator it = j.end();
CHECK(it.m_it.primitive_iterator == 1);
CHECK((it.m_it.primitive_iterator.m_it == 1));
}
SECTION("number")
{
json j(17);
json::iterator it = j.end();
CHECK(it.m_it.primitive_iterator == 1);
CHECK((it.m_it.primitive_iterator.m_it == 1));
it--;
CHECK(it.m_it.primitive_iterator == 0);
CHECK((it.m_it.primitive_iterator.m_it == 0));
it--;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1));
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::iterator it = j.end();
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end());
CHECK((it.m_it.object_iterator == it.m_object->m_value.object->end()));
it--;
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin());
CHECK((it.m_it.object_iterator == it.m_object->m_value.object->begin()));
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::iterator it = j.end();
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator == it.m_object->m_value.array->end()));
it--;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
it--;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
it--;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
it--;
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator == it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
}
}
@ -355,46 +355,46 @@ TEST_CASE("iterator class")
{
json j(json::value_t::null);
json::iterator it = j.end();
CHECK(it.m_it.primitive_iterator == 1);
CHECK((it.m_it.primitive_iterator.m_it == 1));
}
SECTION("number")
{
json j(17);
json::iterator it = j.end();
CHECK(it.m_it.primitive_iterator == 1);
CHECK((it.m_it.primitive_iterator.m_it == 1));
--it;
CHECK(it.m_it.primitive_iterator == 0);
CHECK((it.m_it.primitive_iterator.m_it == 0));
--it;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1));
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::iterator it = j.end();
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end());
CHECK((it.m_it.object_iterator == it.m_object->m_value.object->end()));
--it;
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin());
CHECK((it.m_it.object_iterator == it.m_object->m_value.object->begin()));
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::iterator it = j.end();
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator == it.m_object->m_value.array->end()));
--it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
--it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
--it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
--it;
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
CHECK((it.m_it.array_iterator == it.m_object->m_value.array->begin()));
CHECK((it.m_it.array_iterator != it.m_object->m_value.array->end()));
}
}
}

View file

@ -38,86 +38,86 @@ TEST_CASE("lexer class")
{
SECTION("structural characters")
{
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("["),
1).scan() == json::lexer::token_type::begin_array);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("]"),
1).scan() == json::lexer::token_type::end_array);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("{"),
1).scan() == json::lexer::token_type::begin_object);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("}"),
1).scan() == json::lexer::token_type::end_object);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(","),
1).scan() == json::lexer::token_type::value_separator);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(":"),
1).scan() == json::lexer::token_type::name_separator);
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("["),
1).scan() == json::lexer::token_type::begin_array));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("]"),
1).scan() == json::lexer::token_type::end_array));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("{"),
1).scan() == json::lexer::token_type::begin_object));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("}"),
1).scan() == json::lexer::token_type::end_object));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(","),
1).scan() == json::lexer::token_type::value_separator));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(":"),
1).scan() == json::lexer::token_type::name_separator));
}
SECTION("literal names")
{
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("null"),
4).scan() == json::lexer::token_type::literal_null);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("true"),
4).scan() == json::lexer::token_type::literal_true);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("false"),
5).scan() == json::lexer::token_type::literal_false);
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("null"),
4).scan() == json::lexer::token_type::literal_null));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("true"),
4).scan() == json::lexer::token_type::literal_true));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("false"),
5).scan() == json::lexer::token_type::literal_false));
}
SECTION("numbers")
{
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("0"),
1).scan() == json::lexer::token_type::value_number);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("1"),
1).scan() == json::lexer::token_type::value_number);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("2"),
1).scan() == json::lexer::token_type::value_number);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("3"),
1).scan() == json::lexer::token_type::value_number);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("4"),
1).scan() == json::lexer::token_type::value_number);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("5"),
1).scan() == json::lexer::token_type::value_number);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("6"),
1).scan() == json::lexer::token_type::value_number);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("7"),
1).scan() == json::lexer::token_type::value_number);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("8"),
1).scan() == json::lexer::token_type::value_number);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("9"),
1).scan() == json::lexer::token_type::value_number);
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("0"),
1).scan() == json::lexer::token_type::value_number));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("1"),
1).scan() == json::lexer::token_type::value_number));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("2"),
1).scan() == json::lexer::token_type::value_number));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("3"),
1).scan() == json::lexer::token_type::value_number));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("4"),
1).scan() == json::lexer::token_type::value_number));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("5"),
1).scan() == json::lexer::token_type::value_number));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("6"),
1).scan() == json::lexer::token_type::value_number));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("7"),
1).scan() == json::lexer::token_type::value_number));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("8"),
1).scan() == json::lexer::token_type::value_number));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("9"),
1).scan() == json::lexer::token_type::value_number));
}
SECTION("whitespace")
{
// result is end_of_input, because not token is following
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(" "),
1).scan() == json::lexer::token_type::end_of_input);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\t"),
1).scan() == json::lexer::token_type::end_of_input);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\n"),
1).scan() == json::lexer::token_type::end_of_input);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\r"),
1).scan() == json::lexer::token_type::end_of_input);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(" \t\n\r\n\t "),
7).scan() == json::lexer::token_type::end_of_input);
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(" "),
1).scan() == json::lexer::token_type::end_of_input));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\t"),
1).scan() == json::lexer::token_type::end_of_input));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\n"),
1).scan() == json::lexer::token_type::end_of_input));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\r"),
1).scan() == json::lexer::token_type::end_of_input));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(" \t\n\r\n\t "),
7).scan() == json::lexer::token_type::end_of_input));
}
}
SECTION("token_type_name")
{
CHECK(json::lexer::token_type_name(json::lexer::token_type::uninitialized) == "<uninitialized>");
CHECK(json::lexer::token_type_name(json::lexer::token_type::literal_true) == "true literal");
CHECK(json::lexer::token_type_name(json::lexer::token_type::literal_false) == "false literal");
CHECK(json::lexer::token_type_name(json::lexer::token_type::literal_null) == "null literal");
CHECK(json::lexer::token_type_name(json::lexer::token_type::value_string) == "string literal");
CHECK(json::lexer::token_type_name(json::lexer::token_type::value_number) == "number literal");
CHECK(json::lexer::token_type_name(json::lexer::token_type::begin_array) == "'['");
CHECK(json::lexer::token_type_name(json::lexer::token_type::begin_object) == "'{'");
CHECK(json::lexer::token_type_name(json::lexer::token_type::end_array) == "']'");
CHECK(json::lexer::token_type_name(json::lexer::token_type::end_object) == "'}'");
CHECK(json::lexer::token_type_name(json::lexer::token_type::name_separator) == "':'");
CHECK(json::lexer::token_type_name(json::lexer::token_type::value_separator) == "','");
CHECK(json::lexer::token_type_name(json::lexer::token_type::parse_error) == "<parse error>");
CHECK(json::lexer::token_type_name(json::lexer::token_type::end_of_input) == "end of input");
CHECK((json::lexer::token_type_name(json::lexer::token_type::uninitialized) == "<uninitialized>"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::literal_true) == "true literal"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::literal_false) == "false literal"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::literal_null) == "null literal"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::value_string) == "string literal"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::value_number) == "number literal"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::begin_array) == "'['"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::begin_object) == "'{'"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::end_array) == "']'"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::end_object) == "'}'"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::name_separator) == "':'"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::value_separator) == "','"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::parse_error) == "<parse error>"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::end_of_input) == "end of input"));
}
SECTION("parse errors on first character")
@ -150,7 +150,7 @@ TEST_CASE("lexer class")
case ('8'):
case ('9'):
{
CHECK(res != json::lexer::token_type::parse_error);
CHECK((res != json::lexer::token_type::parse_error));
break;
}
@ -160,14 +160,14 @@ TEST_CASE("lexer class")
case ('\n'):
case ('\r'):
{
CHECK(res == json::lexer::token_type::end_of_input);
CHECK((res == json::lexer::token_type::end_of_input));
break;
}
// anything else is not expected
default:
{
CHECK(res == json::lexer::token_type::parse_error);
CHECK((res == json::lexer::token_type::parse_error));
break;
}
}

View file

@ -160,12 +160,30 @@ TEST_CASE("value conversion")
{
std::forward_list<json> a = j.get<std::forward_list<json>>();
CHECK(json(a) == j);
CHECK_THROWS_AS(json(json::value_t::null).get<std::forward_list<json>>(), std::logic_error);
CHECK_THROWS_WITH(json(json::value_t::null).get<std::forward_list<json>>(),
"type must be array, but is null");
}
SECTION("std::vector<json>")
{
std::vector<json> a = j.get<std::vector<json>>();
CHECK(json(a) == j);
CHECK_THROWS_AS(json(json::value_t::null).get<std::vector<json>>(), std::logic_error);
CHECK_THROWS_WITH(json(json::value_t::null).get<std::vector<json>>(),
"type must be array, but is null");
#if not defined(JSON_NOEXCEPTION)
SECTION("reserve is called on containers that supports it")
{
// making the call to from_json throw in order to check capacity
std::vector<float> v;
CHECK_THROWS_AS(nlohmann::from_json(j, v), std::logic_error);
CHECK(v.capacity() == j.size());
}
#endif
}
SECTION("std::deque<json>")
@ -184,6 +202,8 @@ TEST_CASE("value conversion")
CHECK_THROWS_AS(json(json::value_t::number_unsigned).get<json::array_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::number_float).get<json::array_t>(), std::logic_error);
CHECK_THROWS_WITH(json(json::value_t::object).get<std::vector<int>>(),
"type must be array, but is object");
CHECK_THROWS_WITH(json(json::value_t::null).get<json::array_t>(),
"type must be array, but is null");
CHECK_THROWS_WITH(json(json::value_t::object).get<json::array_t>(),
@ -1004,6 +1024,8 @@ TEST_CASE("value conversion")
CHECK_THROWS_AS((json().get<std::vector<json>>()), std::logic_error);
CHECK_THROWS_AS((json().get<std::list<json>>()), std::logic_error);
// does type really must be an array? or it rather must not be null?
// that's what I thought when other test like this one broke
CHECK_THROWS_WITH((json().get<std::list<int>>()), "type must be array, but is null");
CHECK_THROWS_WITH((json().get<std::vector<int>>()), "type must be array, but is null");
CHECK_THROWS_WITH((json().get<std::vector<json>>()), "type must be array, but is null");

View file

@ -0,0 +1,32 @@
#include "catch.hpp"
#include "json.hpp"
using nlohmann::json;
enum test
{
};
struct pod {};
struct pod_bis {};
void to_json(json&, pod) noexcept;
void to_json(json&, pod_bis);
void from_json(const json&, pod) noexcept;
void from_json(const json&, pod_bis);
static json j;
static_assert(noexcept(json{}), "");
static_assert(noexcept(nlohmann::to_json(j, 2)), "");
static_assert(noexcept(nlohmann::to_json(j, 2.5)), "");
static_assert(noexcept(nlohmann::to_json(j, true)), "");
static_assert(noexcept(nlohmann::to_json(j, test{})), "");
static_assert(noexcept(nlohmann::to_json(j, pod{})), "");
static_assert(not noexcept(nlohmann::to_json(j, pod_bis{})), "");
static_assert(noexcept(json(2)), "");
static_assert(noexcept(json(test{})), "");
static_assert(noexcept(json(pod{})), "");
static_assert(noexcept(j.get<pod>()), "");
static_assert(not noexcept(j.get<pod_bis>()), "");
static_assert(noexcept(json(pod{})), "");

View file

@ -63,10 +63,18 @@ TEST_CASE("regression tests")
SECTION("pull request #71 - handle enum type")
{
enum { t = 0 };
enum { t = 0, u = 1};
json j = json::array();
j.push_back(t);
// maybe this is not the place to test this?
json j2 = u;
auto anon_enum_value = j2.get<decltype(u)>();
CHECK(u == anon_enum_value);
static_assert(std::is_same<decltype(anon_enum_value), decltype(u)>::value, "");
j.push_back(json::object(
{
{"game_type", t}

680
test/src/unit-udt.cpp Normal file
View file

@ -0,0 +1,680 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
| | |__ | | | | | | version 2.0.7
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <array>
#include <map>
#include <string>
#include <memory>
#include "catch.hpp"
#include "json.hpp"
using nlohmann::json;
namespace udt
{
enum class country
{
china,
france,
russia
};
struct age
{
int m_val;
};
struct name
{
std::string m_val;
};
struct address
{
std::string m_val;
};
struct person
{
age m_age;
name m_name;
country m_country;
};
struct contact
{
person m_person;
address m_address;
};
struct contact_book
{
name m_book_name;
std::vector<contact> m_contacts;
};
}
// to_json methods
namespace udt
{
// templates because of the custom_json tests (see below)
template <typename BasicJsonType>
void to_json(BasicJsonType& j, age a)
{
j = a.m_val;
}
template <typename BasicJsonType>
void to_json(BasicJsonType& j, const name& n)
{
j = n.m_val;
}
template <typename BasicJsonType>
void to_json(BasicJsonType& j, country c)
{
switch (c)
{
case country::china:
j = u8"中华人民共和国";
return;
case country::france:
j = "France";
return;
case country::russia:
j = u8"Российская Федерация";
return;
}
}
template <typename BasicJsonType>
void to_json(BasicJsonType& j, const person& p)
{
j = BasicJsonType{{"age", p.m_age}, {"name", p.m_name}, {"country", p.m_country}};
}
void to_json(nlohmann::json& j, const address& a)
{
j = a.m_val;
}
void to_json(nlohmann::json& j, const contact& c)
{
j = json{{"person", c.m_person}, {"address", c.m_address}};
}
void to_json(nlohmann::json& j, const contact_book& cb)
{
j = json{{"name", cb.m_book_name}, {"contacts", cb.m_contacts}};
}
// operators
bool operator==(age lhs, age rhs)
{
return lhs.m_val == rhs.m_val;
}
bool operator==(const address& lhs, const address& rhs)
{
return lhs.m_val == rhs.m_val;
}
bool operator==(const name& lhs, const name& rhs)
{
return lhs.m_val == rhs.m_val;
}
bool operator==(const person& lhs, const person& rhs)
{
return std::tie(lhs.m_name, lhs.m_age) == std::tie(rhs.m_name, rhs.m_age);
}
bool operator==(const contact& lhs, const contact& rhs)
{
return std::tie(lhs.m_person, lhs.m_address) ==
std::tie(rhs.m_person, rhs.m_address);
}
bool operator==(const contact_book& lhs, const contact_book& rhs)
{
return std::tie(lhs.m_book_name, lhs.m_contacts) ==
std::tie(rhs.m_book_name, rhs.m_contacts);
}
}
// from_json methods
namespace udt
{
template <typename BasicJsonType>
void from_json(const BasicJsonType& j, age& a)
{
a.m_val = j.template get<int>();
}
template <typename BasicJsonType>
void from_json(const BasicJsonType& j, name& n)
{
n.m_val = j.template get<std::string>();
}
template <typename BasicJsonType>
void from_json(const BasicJsonType& j, country& c)
{
const auto str = j.template get<std::string>();
static const std::map<std::string, country> m =
{
{u8"中华人民共和国", country::china},
{"France", country::france},
{"Российская Федерация", country::russia}
};
const auto it = m.find(str);
// TODO test exceptions
c = it->second;
}
template <typename BasicJsonType>
void from_json(const BasicJsonType& j, person& p)
{
p.m_age = j["age"].template get<age>();
p.m_name = j["name"].template get<name>();
p.m_country = j["country"].template get<country>();
}
void from_json(const nlohmann::json& j, address& a)
{
a.m_val = j.get<std::string>();
}
void from_json(const nlohmann::json& j, contact& c)
{
c.m_person = j["person"].get<person>();
c.m_address = j["address"].get<address>();
}
void from_json(const nlohmann::json& j, contact_book& cb)
{
cb.m_book_name = j["name"].get<name>();
cb.m_contacts = j["contacts"].get<std::vector<contact>>();
}
}
TEST_CASE("basic usage", "[udt]")
{
// a bit narcissic maybe :) ?
const udt::age a
{
23
};
const udt::name n{"theo"};
const udt::country c{udt::country::france};
const udt::person sfinae_addict{a, n, c};
const udt::person senior_programmer{{42}, {u8"王芳"}, udt::country::china};
const udt::address addr{"Paris"};
const udt::contact cpp_programmer{sfinae_addict, addr};
const udt::contact_book book{{"C++"}, {cpp_programmer, {senior_programmer, addr}}};
SECTION("conversion to json via free-functions")
{
CHECK(json(a) == json(23));
CHECK(json(n) == json("theo"));
CHECK(json(c) == json("France"));
CHECK(json(sfinae_addict) == R"({"name":"theo", "age":23, "country":"France"})"_json);
CHECK(json("Paris") == json(addr));
CHECK(json(cpp_programmer) ==
R"({"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"})"_json);
CHECK(
json(book) ==
u8R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"", "name":""}, "address":"Paris"}]})"_json);
}
SECTION("conversion from json via free-functions")
{
const auto big_json =
u8R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"", "name":""}, "address":"Paris"}]})"_json;
SECTION("via explicit calls to get")
{
const auto parsed_book = big_json.get<udt::contact_book>();
const auto book_name = big_json["name"].get<udt::name>();
const auto contacts =
big_json["contacts"].get<std::vector<udt::contact>>();
const auto contact_json = big_json["contacts"].at(0);
const auto contact = contact_json.get<udt::contact>();
const auto person = contact_json["person"].get<udt::person>();
const auto address = contact_json["address"].get<udt::address>();
const auto age = contact_json["person"]["age"].get<udt::age>();
const auto country =
contact_json["person"]["country"].get<udt::country>();
const auto name = contact_json["person"]["name"].get<udt::name>();
CHECK(age == a);
CHECK(name == n);
CHECK(country == c);
CHECK(address == addr);
CHECK(person == sfinae_addict);
CHECK(contact == cpp_programmer);
CHECK(contacts == book.m_contacts);
CHECK(book_name == udt::name{"C++"});
CHECK(book == parsed_book);
}
SECTION("implicit conversions")
{
const udt::contact_book parsed_book = big_json;
const udt::name book_name = big_json["name"];
const std::vector<udt::contact> contacts = big_json["contacts"];
const auto contact_json = big_json["contacts"].at(0);
const udt::contact contact = contact_json;
const udt::person person = contact_json["person"];
const udt::address address = contact_json["address"];
const udt::age age = contact_json["person"]["age"];
const udt::country country = contact_json["person"]["country"];
const udt::name name = contact_json["person"]["name"];
CHECK(age == a);
CHECK(name == n);
CHECK(country == c);
CHECK(address == addr);
CHECK(person == sfinae_addict);
CHECK(contact == cpp_programmer);
CHECK(contacts == book.m_contacts);
CHECK(book_name == udt::name{"C++"});
CHECK(book == parsed_book);
}
}
}
namespace udt
{
struct legacy_type
{
std::string number;
};
}
namespace nlohmann
{
template <typename T>
struct adl_serializer<std::shared_ptr<T>>
{
static void to_json(json& j, const std::shared_ptr<T>& opt)
{
if (opt)
{
j = *opt;
}
else
{
j = nullptr;
}
}
static void from_json(const json& j, std::shared_ptr<T>& opt)
{
if (j.is_null())
{
opt = nullptr;
}
else
{
opt.reset(new T(j.get<T>()));
}
}
};
template <>
struct adl_serializer<udt::legacy_type>
{
static void to_json(json& j, const udt::legacy_type& l)
{
j = std::stoi(l.number);
}
static void from_json(const json& j, udt::legacy_type& l)
{
l.number = std::to_string(j.get<int>());
}
};
}
TEST_CASE("adl_serializer specialization", "[udt]")
{
SECTION("partial specialization")
{
SECTION("to_json")
{
std::shared_ptr<udt::person> optPerson;
json j = optPerson;
CHECK(j.is_null());
optPerson.reset(new udt::person{{42}, {"John Doe"}, udt::country::russia});
j = optPerson;
CHECK_FALSE(j.is_null());
CHECK(j.get<udt::person>() == *optPerson);
}
SECTION("from_json")
{
auto person = udt::person{{42}, {"John Doe"}, udt::country::russia};
json j = person;
auto optPerson = j.get<std::shared_ptr<udt::person>>();
REQUIRE(optPerson);
CHECK(*optPerson == person);
j = nullptr;
optPerson = j.get<std::shared_ptr<udt::person>>();
CHECK(!optPerson);
}
}
SECTION("total specialization")
{
SECTION("to_json")
{
udt::legacy_type lt{"4242"};
json j = lt;
CHECK(j.get<int>() == 4242);
}
SECTION("from_json")
{
json j = 4242;
auto lt = j.get<udt::legacy_type>();
CHECK(lt.number == "4242");
}
}
}
namespace nlohmann
{
template <>
struct adl_serializer<std::vector<float>>
{
using type = std::vector<float>;
static void to_json(json& j, const type&)
{
j = "hijacked!";
}
static void from_json(const json&, type& opt)
{
opt = {42.0, 42.0, 42.0};
}
// preferred version
static type from_json(const json&)
{
return {4.0, 5.0, 6.0};
}
};
}
TEST_CASE("even supported types can be specialized", "[udt]")
{
json j = std::vector<float> {1.0, 2.0, 3.0};
CHECK(j.dump() == R"("hijacked!")");
auto f = j.get<std::vector<float>>();
// the single argument from_json method is preferred
CHECK((f == std::vector<float> {4.0, 5.0, 6.0}));
}
namespace nlohmann
{
template <typename T>
struct adl_serializer<std::unique_ptr<T>>
{
static void to_json(json& j, const std::unique_ptr<T>& opt)
{
if (opt)
{
j = *opt;
}
else
{
j = nullptr;
}
}
// this is the overload needed for non-copyable types,
static std::unique_ptr<T> from_json(const json& j)
{
if (j.is_null())
{
return nullptr;
}
else
{
return std::unique_ptr<T>(new T(j.get<T>()));
}
}
};
}
TEST_CASE("Non-copyable types", "[udt]")
{
SECTION("to_json")
{
std::unique_ptr<udt::person> optPerson;
json j = optPerson;
CHECK(j.is_null());
optPerson.reset(new udt::person{{42}, {"John Doe"}, udt::country::russia});
j = optPerson;
CHECK_FALSE(j.is_null());
CHECK(j.get<udt::person>() == *optPerson);
}
SECTION("from_json")
{
auto person = udt::person{{42}, {"John Doe"}, udt::country::russia};
json j = person;
auto optPerson = j.get<std::unique_ptr<udt::person>>();
REQUIRE(optPerson);
CHECK(*optPerson == person);
j = nullptr;
optPerson = j.get<std::unique_ptr<udt::person>>();
CHECK(!optPerson);
}
}
// custom serializer - advanced usage
// pack structs that are pod-types (but not scalar types)
// relies on adl for any other type
template <typename T, typename = void>
struct pod_serializer
{
// use adl for non-pods, or scalar types
template <
typename BasicJsonType, typename U = T,
typename std::enable_if <
not(std::is_pod<U>::value and std::is_class<U>::value), int >::type = 0 >
static void from_json(const BasicJsonType& j, U& t)
{
using nlohmann::from_json;
from_json(j, t);
}
// special behaviour for pods
template <typename BasicJsonType, typename U = T,
typename std::enable_if<
std::is_pod<U>::value and std::is_class<U>::value, int>::type = 0>
static void from_json(const BasicJsonType& j, U& t)
{
std::uint64_t value;
// TODO The following block is no longer relevant in this serializer, make another one that shows the issue
// the problem arises only when one from_json method is defined without any constraint
//
// Why cannot we simply use: j.get<std::uint64_t>() ?
// Well, with the current experiment, the get method looks for a from_json
// function, which we are currently defining!
// This would end up in a stack overflow. Calling nlohmann::from_json is a
// workaround (is it?).
// I shall find a good way to avoid this once all constructors are converted
// to free methods
//
// In short, constructing a json by constructor calls to_json
// calling get calls from_json, for now, we cannot do this in custom
// serializers
nlohmann::from_json(j, value);
auto bytes = static_cast<char*>(static_cast<void*>(&value));
std::memcpy(&t, bytes, sizeof(value));
}
template <
typename BasicJsonType, typename U = T,
typename std::enable_if <
not(std::is_pod<U>::value and std::is_class<U>::value), int >::type = 0 >
static void to_json(BasicJsonType& j, const T& t)
{
using nlohmann::to_json;
to_json(j, t);
}
template <typename BasicJsonType, typename U = T,
typename std::enable_if<
std::is_pod<U>::value and std::is_class<U>::value, int>::type = 0>
static void to_json(BasicJsonType& j, const T& t) noexcept
{
auto bytes = static_cast< const unsigned char*>(static_cast<const void*>(&t));
std::uint64_t value = bytes[0];
for (auto i = 1; i < 8; ++i)
value |= std::uint64_t{bytes[i]} << 8 * i;
nlohmann::to_json(j, value);
}
};
namespace udt
{
struct small_pod
{
int begin;
char middle;
short end;
};
struct non_pod
{
std::string s;
};
template <typename BasicJsonType>
void to_json(BasicJsonType& j, const non_pod& np)
{
j = np.s;
}
template <typename BasicJsonType>
void from_json(const BasicJsonType& j, non_pod& np)
{
np.s = j.template get<std::string>();
}
bool operator==(small_pod lhs, small_pod rhs) noexcept
{
return std::tie(lhs.begin, lhs.middle, lhs.end) ==
std::tie(rhs.begin, rhs.middle, rhs.end);
}
bool operator==(const non_pod& lhs, const non_pod& rhs) noexcept
{
return lhs.s == rhs.s;
}
std::ostream& operator<<(std::ostream& os, small_pod l)
{
return os << "begin: " << l.begin << ", middle: " << l.middle << ", end: " << l.end;
}
}
TEST_CASE("custom serializer for pods", "[udt]")
{
using custom_json =
nlohmann::basic_json<std::map, std::vector, std::string, bool,
std::int64_t, std::uint64_t, double, std::allocator,
pod_serializer>;
auto p = udt::small_pod{42, '/', 42};
custom_json j = p;
auto p2 = j.get<udt::small_pod>();
CHECK(p == p2);
auto np = udt::non_pod{{"non-pod"}};
custom_json j2 = np;
auto np2 = j2.get<udt::non_pod>();
CHECK(np == np2);
}
template <typename T, typename>
struct another_adl_serializer;
using custom_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, double, std::allocator, another_adl_serializer>;
template <typename T, typename>
struct another_adl_serializer
{
static void from_json(const custom_json& j, T& t)
{
using nlohmann::from_json;
from_json(j, t);
}
static void to_json(custom_json& j, const T& t)
{
using nlohmann::to_json;
to_json(j, t);
}
};
TEST_CASE("custom serializer that does adl by default", "[udt]")
{
using json = nlohmann::json;
auto me = udt::person{{23}, {"theo"}, udt::country::france};
json j = me;
custom_json cj = me;
CHECK(j.dump() == cj.dump());
CHECK(me == j.get<udt::person>());
CHECK(me == cj.get<udt::person>());
}