:twisted_rightwards_arrows: merge branch 'release/2.1.0'
diff --git a/.travis.yml b/.travis.yml
index 6bf2297..ef5c614 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -71,6 +71,21 @@
     after_success:
       - make cppcheck
 
+  # no exceptions
+
+  - os: linux
+    compiler: gcc
+    env:
+      - COMPILER=g++-4.9
+      - SPECIAL=no_exceptions
+      - TEST_PATTERN=-e \"*\"
+    addons:
+      apt:
+        sources: ['ubuntu-toolchain-r-test']
+        packages: [g++-4.9, cppcheck]
+    before_script:
+      - CPPFLAGS="-DJSON_NOEXCEPTION" make
+
   # Coveralls (http://gronlier.fr/blog/2015/01/adding-code-coverage-to-your-c-project/)
 
   - os: linux
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9f9931c..f1ccf47 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,7 @@
 cmake_minimum_required(VERSION 3.0)
 
 # define the project
-project(nlohmann_json VERSION 2.0.10 LANGUAGES CXX)
+project(nlohmann_json VERSION 2.1.0 LANGUAGES CXX)
 
 enable_testing()
 
diff --git a/ChangeLog.md b/ChangeLog.md
index ce82d14..0e2f8da 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,6 +1,43 @@
 # Change Log
 All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).
 
+## [v2.1.0](https://github.com/nlohmann/json/releases/tag/v2.1.0) (2017-01-28)
+[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.10...v2.1.0)
+
+- Parsing multiple JSON objects from a string or stream [\#438](https://github.com/nlohmann/json/issues/438)
+- Use-of-uninitialized-value \(OSS-Fuzz issue 477\) [\#437](https://github.com/nlohmann/json/issues/437)
+- add `reserve` function for array to reserve memory before adding json values into it [\#436](https://github.com/nlohmann/json/issues/436)
+- Typo in examples page [\#434](https://github.com/nlohmann/json/issues/434)
+- avoid malformed json [\#433](https://github.com/nlohmann/json/issues/433)
+- How to add json objects to a map? [\#432](https://github.com/nlohmann/json/issues/432)
+- create json instance from raw json \(unsigned char\*\) [\#431](https://github.com/nlohmann/json/issues/431)
+- Getting std::invalid\_argument: stream error when following example [\#429](https://github.com/nlohmann/json/issues/429)
+- Forward declare-only header? [\#427](https://github.com/nlohmann/json/issues/427)
+- Implicit conversion from array to object [\#425](https://github.com/nlohmann/json/issues/425)
+- Automatic ordered JSON [\#424](https://github.com/nlohmann/json/issues/424)
+- error C4996: 'strerror' when reading file [\#422](https://github.com/nlohmann/json/issues/422)
+- Get an error - JSON pointer must be empty or begin with '/' [\#421](https://github.com/nlohmann/json/issues/421)
+- size parameter for parse\(\) [\#419](https://github.com/nlohmann/json/issues/419)
+- json.hpp forcibly defines GCC\_VERSION [\#417](https://github.com/nlohmann/json/issues/417)
+- Use-of-uninitialized-value \(OSS-Fuzz issue 377\) [\#416](https://github.com/nlohmann/json/issues/416)
+- Single char converted to ASCII code instead of string [\#413](https://github.com/nlohmann/json/issues/413)
+- How to know if a string  was parsed as utf-8? [\#406](https://github.com/nlohmann/json/issues/406)
+- Overloaded += to add objects to an array makes no sense? [\#404](https://github.com/nlohmann/json/issues/404)
+- Finding a value in an array [\#399](https://github.com/nlohmann/json/issues/399)
+- add release information in static function [\#397](https://github.com/nlohmann/json/issues/397)
+- Optimize memory usage of json objects in combination with binary serialization [\#373](https://github.com/nlohmann/json/issues/373)
+- Conversion operators not considered [\#369](https://github.com/nlohmann/json/issues/369)
+- Append ".0" to serialized floating\_point values that are digits-only. [\#362](https://github.com/nlohmann/json/issues/362)
+- Add a customization point for user-defined types [\#328](https://github.com/nlohmann/json/issues/328)
+- Conformance report for reference [\#307](https://github.com/nlohmann/json/issues/307)
+- Document the best way to serialize/deserialize user defined types to json [\#298](https://github.com/nlohmann/json/issues/298)
+- Add StringView template typename to basic\_json [\#297](https://github.com/nlohmann/json/issues/297)
+- \[Improvement\] Add option to remove exceptions [\#296](https://github.com/nlohmann/json/issues/296)
+- Performance in miloyip/nativejson-benchmark [\#202](https://github.com/nlohmann/json/issues/202)
+
+- conversion from/to user-defined types [\#435](https://github.com/nlohmann/json/pull/435) ([nlohmann](https://github.com/nlohmann))
+- Fix documentation error [\#430](https://github.com/nlohmann/json/pull/430) ([vjon](https://github.com/vjon))
+
 ## [v2.0.10](https://github.com/nlohmann/json/releases/tag/v2.0.10) (2017-01-02)
 [Full Changelog](https://github.com/nlohmann/json/compare/v2.0.9...v2.0.10)
 
diff --git a/LICENSE.MIT b/LICENSE.MIT
index c4ce40d..00599af 100644
--- a/LICENSE.MIT
+++ b/LICENSE.MIT
@@ -1,14 +1,13 @@
-JSON for Modern C++ is licensed under the MIT License 
-<http://opensource.org/licenses/MIT>:
+MIT License 
 
 Copyright (c) 2013-2017 Niels Lohmann
 
-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:
+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.
diff --git a/README.md b/README.md
index 498d1bb..1ee358e 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
 [![Build status](https://doozer.io/badge/nlohmann/json/buildstatus/develop)](https://doozer.io/user/nlohmann/json)
 [![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json)
 [![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json)
-[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/IoZNMHqubixQx2dN)
+[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/4NEU6ZZMoM9lpIex)
 [![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json)
 [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT)
 [![Github Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases)
@@ -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)
@@ -263,6 +264,9 @@
 j[1] = 42;
 bool foo = j.at(2);
 
+// comparison
+j == "[\"foo\", 1, true]"_json;  // true
+
 // other stuff
 j.size();     // 3 entries
 j.empty();    // false
@@ -277,9 +281,6 @@
 j.is_array();
 j.is_string();
 
-// comparison
-j == "[\"foo\", 1, true]"_json;  // true
-
 // create an object
 json o;
 o["foo"] = 23;
@@ -442,6 +443,224 @@
 // 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.
@@ -521,7 +740,7 @@
 
 The class is licensed under the [MIT License](http://opensource.org/licenses/MIT):
 
-Copyright &copy; 2013-2016 [Niels Lohmann](http://nlohmann.me)
+Copyright &copy; 2013-2017 [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:
 
@@ -546,7 +765,7 @@
 - [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 @@
 - [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.
@@ -587,8 +806,13 @@
 - [Yixin Zhang](https://github.com/qwename) fixed an integer overflow check.
 - [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.
+- [EnricoBilla](https://github.com/EnricoBilla) noted a typo in an example.
 
-Thanks a lot for helping out!
+Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone.
 
 
 ## Notes
@@ -600,6 +824,9 @@
   - Other encodings such as Latin-1, UTF-16, or UTF-32 are not supported and will yield parse errors.
   - [Unicode noncharacters](http://www.unicode.org/faq/private_use.html#nonchar1) will not be replaced by the library.
   - Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors.
+  - The strings stored in the library are UTF-8 encoded. When using the default string type (`std::string`), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs.
+- The code can be compiled without C++ **runtime type identification** features; that is, you can use the `-fno-rtti` compiler flag.
+- **Exceptions** are used widly within the library. They can, however, be switched off with either using the compiler flag `-fno-exceptions` or by defining the symbol `JSON_NOEXCEPTION`. In this case, exceptions are replaced by an `abort()` call.
 
 
 ## Execute unit tests
@@ -607,10 +834,11 @@
 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
diff --git a/doc/Doxyfile b/doc/Doxyfile
index 5064a0a..b4f5786 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -5,7 +5,7 @@
 #---------------------------------------------------------------------------
 DOXYFILE_ENCODING      = UTF-8
 PROJECT_NAME           = "JSON for Modern C++"
-PROJECT_NUMBER         = 2.0.10
+PROJECT_NUMBER         = 2.1.0
 PROJECT_BRIEF          = 
 PROJECT_LOGO           =
 OUTPUT_DIRECTORY       = .
@@ -109,7 +109,7 @@
 EXCLUDE                =
 EXCLUDE_SYMLINKS       = NO
 EXCLUDE_PATTERNS       =
-EXCLUDE_SYMBOLS        = nlohmann::anonymous_namespace
+EXCLUDE_SYMBOLS        = nlohmann::detail
 EXAMPLE_PATH           = examples
 EXAMPLE_PATTERNS       =
 EXAMPLE_RECURSIVE      = NO
diff --git a/doc/examples/README.link b/doc/examples/README.link
index 58daac8..d0168aa 100644
--- a/doc/examples/README.link
+++ b/doc/examples/README.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/IoZNMHqubixQx2dN"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/4NEU6ZZMoM9lpIex"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/at__object_t_key_type.cpp b/doc/examples/at__object_t_key_type.cpp
index a8fc999..83646f1 100644
--- a/doc/examples/at__object_t_key_type.cpp
+++ b/doc/examples/at__object_t_key_type.cpp
@@ -8,7 +8,7 @@
     json object =
     {
         {"the good", "il buono"},
-        {"the bad", "il cativo"},
+        {"the bad", "il cattivo"},
         {"the ugly", "il brutto"}
     };
 
diff --git a/doc/examples/at__object_t_key_type.link b/doc/examples/at__object_t_key_type.link
index e01197e..4a050ec 100644
--- a/doc/examples/at__object_t_key_type.link
+++ b/doc/examples/at__object_t_key_type.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/rsnwWxpDjmtRRSzb"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/tb5CaFfsMWpAvi7m"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/at__object_t_key_type_const.cpp b/doc/examples/at__object_t_key_type_const.cpp
index 0686db4..0e8d9d7 100644
--- a/doc/examples/at__object_t_key_type_const.cpp
+++ b/doc/examples/at__object_t_key_type_const.cpp
@@ -8,7 +8,7 @@
     json object =
     {
         {"the good", "il buono"},
-        {"the bad", "il cativo"},
+        {"the bad", "il cattivo"},
         {"the ugly", "il brutto"}
     };
 
diff --git a/doc/examples/at__object_t_key_type_const.link b/doc/examples/at__object_t_key_type_const.link
index 2588529..1ad9c07 100644
--- a/doc/examples/at__object_t_key_type_const.link
+++ b/doc/examples/at__object_t_key_type_const.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/aeoZrnhnb3HKClCY"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/NFG86H5khRUePc1s"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__CompatibleArrayType.cpp b/doc/examples/basic_json__CompatibleArrayType.cpp
deleted file mode 100644
index 26a1a10..0000000
--- a/doc/examples/basic_json__CompatibleArrayType.cpp
+++ /dev/null
@@ -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';
-}
diff --git a/doc/examples/basic_json__CompatibleArrayType.link b/doc/examples/basic_json__CompatibleArrayType.link
deleted file mode 100644
index 95ecb05..0000000
--- a/doc/examples/basic_json__CompatibleArrayType.link
+++ /dev/null
@@ -1 +0,0 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/3BIhBw91FUVuHE1D"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__CompatibleArrayType.output b/doc/examples/basic_json__CompatibleArrayType.output
deleted file mode 100644
index 428505a..0000000
--- a/doc/examples/basic_json__CompatibleArrayType.output
+++ /dev/null
@@ -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"]
diff --git a/doc/examples/basic_json__CompatibleIntegerNumberType.cpp b/doc/examples/basic_json__CompatibleIntegerNumberType.cpp
deleted file mode 100644
index 50e751d..0000000
--- a/doc/examples/basic_json__CompatibleIntegerNumberType.cpp
+++ /dev/null
@@ -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';
-}
diff --git a/doc/examples/basic_json__CompatibleIntegerNumberType.link b/doc/examples/basic_json__CompatibleIntegerNumberType.link
deleted file mode 100644
index 7a37e9e..0000000
--- a/doc/examples/basic_json__CompatibleIntegerNumberType.link
+++ /dev/null
@@ -1 +0,0 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/PcMzhcu2RpD7KSwr"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__CompatibleIntegerNumberType.output b/doc/examples/basic_json__CompatibleIntegerNumberType.output
deleted file mode 100644
index c7f24d6..0000000
--- a/doc/examples/basic_json__CompatibleIntegerNumberType.output
+++ /dev/null
@@ -1,5 +0,0 @@
-42
-23
-1024
-17
-8
diff --git a/doc/examples/basic_json__CompatibleNumberFloatType.cpp b/doc/examples/basic_json__CompatibleNumberFloatType.cpp
deleted file mode 100644
index 6f8d3f6..0000000
--- a/doc/examples/basic_json__CompatibleNumberFloatType.cpp
+++ /dev/null
@@ -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';
-}
diff --git a/doc/examples/basic_json__CompatibleNumberFloatType.link b/doc/examples/basic_json__CompatibleNumberFloatType.link
deleted file mode 100644
index 9fbc731..0000000
--- a/doc/examples/basic_json__CompatibleNumberFloatType.link
+++ /dev/null
@@ -1 +0,0 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/2TCYWSrOxnR05AZI"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__CompatibleNumberFloatType.output b/doc/examples/basic_json__CompatibleNumberFloatType.output
deleted file mode 100644
index 64bb796..0000000
--- a/doc/examples/basic_json__CompatibleNumberFloatType.output
+++ /dev/null
@@ -1,3 +0,0 @@
-42.2299995422363
-null
-23.42
diff --git a/doc/examples/basic_json__CompatibleObjectType.cpp b/doc/examples/basic_json__CompatibleObjectType.cpp
deleted file mode 100644
index d284b69..0000000
--- a/doc/examples/basic_json__CompatibleObjectType.cpp
+++ /dev/null
@@ -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';
-}
diff --git a/doc/examples/basic_json__CompatibleObjectType.link b/doc/examples/basic_json__CompatibleObjectType.link
deleted file mode 100644
index 7512fb3..0000000
--- a/doc/examples/basic_json__CompatibleObjectType.link
+++ /dev/null
@@ -1 +0,0 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/JzLCMcSXNsh4uVWa"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__CompatibleObjectType.output b/doc/examples/basic_json__CompatibleObjectType.output
deleted file mode 100644
index c70f718..0000000
--- a/doc/examples/basic_json__CompatibleObjectType.output
+++ /dev/null
@@ -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}
diff --git a/doc/examples/basic_json__CompatibleStringType.cpp b/doc/examples/basic_json__CompatibleStringType.cpp
deleted file mode 100644
index a0f3b4f..0000000
--- a/doc/examples/basic_json__CompatibleStringType.cpp
+++ /dev/null
@@ -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';
-}
diff --git a/doc/examples/basic_json__CompatibleStringType.link b/doc/examples/basic_json__CompatibleStringType.link
deleted file mode 100644
index 351d6c0..0000000
--- a/doc/examples/basic_json__CompatibleStringType.link
+++ /dev/null
@@ -1 +0,0 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/b9hbCY8zfOiTOuoK"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__CompatibleStringType.output b/doc/examples/basic_json__CompatibleStringType.output
deleted file mode 100644
index 1316dd9..0000000
--- a/doc/examples/basic_json__CompatibleStringType.output
+++ /dev/null
@@ -1 +0,0 @@
-"The quick brown fox jumps over the lazy dog."
diff --git a/doc/examples/basic_json__CompatibleType.cpp b/doc/examples/basic_json__CompatibleType.cpp
new file mode 100644
index 0000000..ff564a7
--- /dev/null
+++ b/doc/examples/basic_json__CompatibleType.cpp
@@ -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';
+}
diff --git a/doc/examples/basic_json__CompatibleType.link b/doc/examples/basic_json__CompatibleType.link
new file mode 100644
index 0000000..a78f01b
--- /dev/null
+++ b/doc/examples/basic_json__CompatibleType.link
@@ -0,0 +1 @@
+<a target="_blank" href="http://melpon.org/wandbox/permlink/VM7W2kpE7sIYJ5DW"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__CompatibleType.output b/doc/examples/basic_json__CompatibleType.output
new file mode 100644
index 0000000..d69ff44
--- /dev/null
+++ b/doc/examples/basic_json__CompatibleType.output
@@ -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
diff --git a/doc/examples/basic_json__array_t.cpp b/doc/examples/basic_json__array_t.cpp
deleted file mode 100644
index 1bb6931..0000000
--- a/doc/examples/basic_json__array_t.cpp
+++ /dev/null
@@ -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';
-}
diff --git a/doc/examples/basic_json__array_t.link b/doc/examples/basic_json__array_t.link
deleted file mode 100644
index 70c9cb8..0000000
--- a/doc/examples/basic_json__array_t.link
+++ /dev/null
@@ -1 +0,0 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/dTbSNAvl6TqrMEAn"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__array_t.output b/doc/examples/basic_json__array_t.output
deleted file mode 100644
index d379a75..0000000
--- a/doc/examples/basic_json__array_t.output
+++ /dev/null
@@ -1 +0,0 @@
-["one","two",3,4.5,false]
diff --git a/doc/examples/basic_json__boolean_t.cpp b/doc/examples/basic_json__boolean_t.cpp
deleted file mode 100644
index 38f014e..0000000
--- a/doc/examples/basic_json__boolean_t.cpp
+++ /dev/null
@@ -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';
-}
diff --git a/doc/examples/basic_json__boolean_t.link b/doc/examples/basic_json__boolean_t.link
deleted file mode 100644
index c64e1fc..0000000
--- a/doc/examples/basic_json__boolean_t.link
+++ /dev/null
@@ -1 +0,0 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/VmVl9pyrQp8LyOnU"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__boolean_t.output b/doc/examples/basic_json__boolean_t.output
deleted file mode 100644
index da29283..0000000
--- a/doc/examples/basic_json__boolean_t.output
+++ /dev/null
@@ -1,2 +0,0 @@
-true
-false
diff --git a/doc/examples/basic_json__const_int.cpp b/doc/examples/basic_json__const_int.cpp
deleted file mode 100644
index 7e38544..0000000
--- a/doc/examples/basic_json__const_int.cpp
+++ /dev/null
@@ -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';
-}
diff --git a/doc/examples/basic_json__const_int.link b/doc/examples/basic_json__const_int.link
deleted file mode 100644
index 68a9e23..0000000
--- a/doc/examples/basic_json__const_int.link
+++ /dev/null
@@ -1 +0,0 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/3xQ1qy7BT9OrSSCo"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__const_int.output b/doc/examples/basic_json__const_int.output
deleted file mode 100644
index 98d9bcb..0000000
--- a/doc/examples/basic_json__const_int.output
+++ /dev/null
@@ -1 +0,0 @@
-17
diff --git a/doc/examples/basic_json__number_float_t.cpp b/doc/examples/basic_json__number_float_t.cpp
deleted file mode 100644
index 92533b7..0000000
--- a/doc/examples/basic_json__number_float_t.cpp
+++ /dev/null
@@ -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';
-}
diff --git a/doc/examples/basic_json__number_float_t.link b/doc/examples/basic_json__number_float_t.link
deleted file mode 100644
index 47aa255..0000000
--- a/doc/examples/basic_json__number_float_t.link
+++ /dev/null
@@ -1 +0,0 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/OTgOxjIAKFvxpFdm"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__number_float_t.output b/doc/examples/basic_json__number_float_t.output
deleted file mode 100644
index 964a7b1..0000000
--- a/doc/examples/basic_json__number_float_t.output
+++ /dev/null
@@ -1,3 +0,0 @@
-3.14159265358979
-null
-null
diff --git a/doc/examples/basic_json__number_integer_t.cpp b/doc/examples/basic_json__number_integer_t.cpp
deleted file mode 100644
index 1078f36..0000000
--- a/doc/examples/basic_json__number_integer_t.cpp
+++ /dev/null
@@ -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';
-}
diff --git a/doc/examples/basic_json__number_integer_t.link b/doc/examples/basic_json__number_integer_t.link
deleted file mode 100644
index 5d4499b..0000000
--- a/doc/examples/basic_json__number_integer_t.link
+++ /dev/null
@@ -1 +0,0 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/cCQRCvjXdRM9YpT5"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__number_integer_t.output b/doc/examples/basic_json__number_integer_t.output
deleted file mode 100644
index d81cc07..0000000
--- a/doc/examples/basic_json__number_integer_t.output
+++ /dev/null
@@ -1 +0,0 @@
-42
diff --git a/doc/examples/basic_json__object_t.cpp b/doc/examples/basic_json__object_t.cpp
deleted file mode 100644
index 39e2fcc..0000000
--- a/doc/examples/basic_json__object_t.cpp
+++ /dev/null
@@ -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';
-}
diff --git a/doc/examples/basic_json__object_t.link b/doc/examples/basic_json__object_t.link
deleted file mode 100644
index 2e07a3e..0000000
--- a/doc/examples/basic_json__object_t.link
+++ /dev/null
@@ -1 +0,0 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/DgtHcz5L6JphTOGJ"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__object_t.output b/doc/examples/basic_json__object_t.output
deleted file mode 100644
index 62376d8..0000000
--- a/doc/examples/basic_json__object_t.output
+++ /dev/null
@@ -1 +0,0 @@
-{"one":1,"two":2}
diff --git a/doc/examples/basic_json__string_t.cpp b/doc/examples/basic_json__string_t.cpp
deleted file mode 100644
index 3205f62..0000000
--- a/doc/examples/basic_json__string_t.cpp
+++ /dev/null
@@ -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';
-}
diff --git a/doc/examples/basic_json__string_t.link b/doc/examples/basic_json__string_t.link
deleted file mode 100644
index d7d02f2..0000000
--- a/doc/examples/basic_json__string_t.link
+++ /dev/null
@@ -1 +0,0 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/cwNYP1q2mT8CFLTk"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__string_t.output b/doc/examples/basic_json__string_t.output
deleted file mode 100644
index 8999004..0000000
--- a/doc/examples/basic_json__string_t.output
+++ /dev/null
@@ -1 +0,0 @@
-"The quick brown fox jumps over the lazy doc"
diff --git a/doc/examples/basic_json__string_t_value_type.cpp b/doc/examples/basic_json__string_t_value_type.cpp
deleted file mode 100644
index 5379ca0..0000000
--- a/doc/examples/basic_json__string_t_value_type.cpp
+++ /dev/null
@@ -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';
-}
diff --git a/doc/examples/basic_json__string_t_value_type.link b/doc/examples/basic_json__string_t_value_type.link
deleted file mode 100644
index 5690876..0000000
--- a/doc/examples/basic_json__string_t_value_type.link
+++ /dev/null
@@ -1 +0,0 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/AtV4zVErfLwkileg"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__string_t_value_type.output b/doc/examples/basic_json__string_t_value_type.output
deleted file mode 100644
index 8999004..0000000
--- a/doc/examples/basic_json__string_t_value_type.output
+++ /dev/null
@@ -1 +0,0 @@
-"The quick brown fox jumps over the lazy doc"
diff --git a/doc/examples/meta.cpp b/doc/examples/meta.cpp
new file mode 100644
index 0000000..3a31ca2
--- /dev/null
+++ b/doc/examples/meta.cpp
@@ -0,0 +1,9 @@
+#include <json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    // call meta()
+    std::cout << std::setw(4) << json::meta() << '\n';
+}
diff --git a/doc/examples/meta.link b/doc/examples/meta.link
new file mode 100644
index 0000000..6a5ad0c
--- /dev/null
+++ b/doc/examples/meta.link
@@ -0,0 +1 @@
+<a target="_blank" href="http://melpon.org/wandbox/permlink/3hERJociqLo3vdod"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/meta.output b/doc/examples/meta.output
new file mode 100644
index 0000000..f361bb6
--- /dev/null
+++ b/doc/examples/meta.output
@@ -0,0 +1,17 @@
+{
+    "compiler": {
+        "c++": "201103",
+        "family": "clang",
+        "version": "8.0.0 (clang-800.0.42.1)"
+    },
+    "copyright": "(C) 2013-2017 Niels Lohmann",
+    "name": "JSON for Modern C++",
+    "platform": "apple",
+    "url": "https://github.com/nlohmann/json",
+    "version": {
+        "major": 2,
+        "minor": 1,
+        "patch": 0,
+        "string": "2.1.0"
+    }
+}
diff --git a/doc/examples/type_name.cpp b/doc/examples/type_name.cpp
new file mode 100644
index 0000000..815e92d
--- /dev/null
+++ b/doc/examples/type_name.cpp
@@ -0,0 +1,24 @@
+#include <json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    // create JSON values
+    json j_null;
+    json j_boolean = true;
+    json j_number_integer = 17;
+    json j_number_float = 23.42;
+    json j_object = {{"one", 1}, {"two", 2}};
+    json j_array = {1, 2, 4, 8, 16};
+    json j_string = "Hello, world";
+
+    // call type_name()
+    std::cout << j_null.type_name() << '\n';
+    std::cout << j_boolean.type_name() << '\n';
+    std::cout << j_number_integer.type_name() << '\n';
+    std::cout << j_number_float.type_name() << '\n';
+    std::cout << j_object.type_name() << '\n';
+    std::cout << j_array.type_name() << '\n';
+    std::cout << j_string.type_name() << '\n';
+}
diff --git a/doc/examples/type_name.link b/doc/examples/type_name.link
new file mode 100644
index 0000000..39d1f97
--- /dev/null
+++ b/doc/examples/type_name.link
@@ -0,0 +1 @@
+<a target="_blank" href="http://melpon.org/wandbox/permlink/V6imubWo6Lkp8gk1"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/type_name.output b/doc/examples/type_name.output
new file mode 100644
index 0000000..ad906a4
--- /dev/null
+++ b/doc/examples/type_name.output
@@ -0,0 +1,7 @@
+null
+boolean
+number
+number
+object
+array
+string
diff --git a/doc/index.md b/doc/index.md
index f7ef355..2f12767 100644
--- a/doc/index.md
+++ b/doc/index.md
@@ -277,4 +277,4 @@
 @author [Niels Lohmann](http://nlohmann.me)
 @see https://github.com/nlohmann/json to download the source code
 
-@version 2.0.10
+@version 2.1.0
diff --git a/doc/json.gif b/doc/json.gif
index efd2ee6..9d05cfd 100644
--- a/doc/json.gif
+++ b/doc/json.gif
Binary files differ
diff --git a/src/json.hpp b/src/json.hpp
index 9d48e7a..5fdd83d 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -39,6 +39,7 @@
 #include <cstdint> // int64_t, uint64_t
 #include <cstdlib> // strtod, strtof, strtold, strtoul
 #include <cstring> // strlen
+#include <forward_list> // forward_list
 #include <functional> // function, hash, less
 #include <initializer_list> // initializer_list
 #include <iomanip> // setw
@@ -58,13 +59,11 @@
 
 // exclude unsupported compilers
 #if defined(__clang__)
-    #define CLANG_VERSION (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__)
-    #if CLANG_VERSION < 30400
+    #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400
         #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers"
     #endif
 #elif defined(__GNUC__)
-    #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
-    #if GCC_VERSION < 40900
+    #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40900
         #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers"
     #endif
 #endif
@@ -90,6 +89,17 @@
     #define JSON_DEPRECATED
 #endif
 
+// allow to disable exceptions
+#if not defined(JSON_NOEXCEPTION) || defined(__EXCEPTIONS)
+    #define JSON_THROW(exception) throw exception
+    #define JSON_TRY try
+    #define JSON_CATCH(exception) catch(exception)
+#else
+    #define JSON_THROW(exception) std::abort()
+    #define JSON_TRY if(true)
+    #define JSON_CATCH(exception) if(false)
+#endif
+
 /*!
 @brief namespace for Niels Lohmann
 @see https://github.com/nlohmann
@@ -98,38 +108,841 @@
 namespace nlohmann
 {
 
-
 /*!
 @brief unnamed namespace with internal helper functions
+
+This namespace collects some functions that could not be defined inside the
+@ref basic_json class.
+
+@since version 2.1.0
+*/
+namespace detail
+{
+///////////////////////////
+// JSON type enumeration //
+///////////////////////////
+
+/*!
+@brief the JSON type enumeration
+
+This enumeration collects the different JSON types. It is internally used to
+distinguish the stored values, and the functions @ref basic_json::is_null(),
+@ref basic_json::is_object(), @ref basic_json::is_array(),
+@ref basic_json::is_string(), @ref basic_json::is_boolean(),
+@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),
+@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),
+@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and
+@ref basic_json::is_structured() rely on it.
+
+@note There are three enumeration entries (number_integer, number_unsigned, and
+number_float), because the library distinguishes these three types for numbers:
+@ref basic_json::number_unsigned_t is used for unsigned integers,
+@ref basic_json::number_integer_t is used for signed integers, and
+@ref basic_json::number_float_t is used for floating-point numbers or to
+approximate integers which do not fit in the limits of their respective type.
+
+@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON
+value with the default value for a given type
+
 @since version 1.0.0
 */
-namespace
+enum class value_t : uint8_t
 {
+    null,            ///< null value
+    object,          ///< object (unordered set of name/value pairs)
+    array,           ///< array (ordered collection of values)
+    string,          ///< string value
+    boolean,         ///< boolean value
+    number_integer,  ///< number value (signed integer)
+    number_unsigned, ///< number value (unsigned integer)
+    number_float,    ///< number value (floating-point)
+    discarded        ///< discarded by the the parser callback function
+};
+
+/*!
+@brief comparison operator for JSON types
+
+Returns an ordering that is similar to Python:
+- order: null < boolean < number < object < array < string
+- furthermore, each type is not smaller than itself
+
+@since version 1.0.0
+*/
+inline bool operator<(const value_t lhs, const value_t rhs) noexcept
+{
+    static constexpr std::array<uint8_t, 8> order = {{
+            0, // null
+            3, // object
+            4, // array
+            5, // string
+            1, // boolean
+            2, // integer
+            2, // unsigned
+            2, // float
+        }
+    };
+
+    // discarded values are not comparable
+    if (lhs == value_t::discarded or rhs == value_t::discarded)
+    {
+        return false;
+    }
+
+    return order[static_cast<std::size_t>(lhs)] <
+           order[static_cast<std::size_t>(rhs)];
+}
+
+
+/////////////
+// helpers //
+/////////////
+
+// alias templates to reduce boilerplate
+template<bool B, typename T = void>
+using enable_if_t = typename std::enable_if<B, T>::type;
+
+template<typename T>
+using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+
+// taken from http://stackoverflow.com/a/26936864/266378
+template<typename T>
+using is_unscoped_enum =
+    std::integral_constant<bool, std::is_convertible<T, int>::value and
+    std::is_enum<T>::value>;
+
+/*
+Implementation of two C++17 constructs: conjunction, negation. This is needed
+to avoid evaluating all the traits in a condition
+
+For example: not std::is_same<void, T>::value and has_value_type<T>::value
+will not compile when T = void (on MSVC at least). Whereas
+conjunction<negation<std::is_same<void, T>>, has_value_type<T>>::value will
+stop evaluating if negation<...>::value == false
+
+Please note that those constructs must be used with caution, since symbols can
+become very long quickly (which can slow down compilation and cause MSVC
+internal compiler errors). Only use it when you have to (see example ahead).
+*/
+template<class...> struct conjunction : std::true_type {};
+template<class B1> struct conjunction<B1> : B1 {};
+template<class B1, class... Bn>
+struct conjunction<B1, Bn...> : std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};
+
+template<class B> struct negation : std::integral_constant < bool, !B::value > {};
+
+// dispatch utility (taken from ranges-v3)
+template<unsigned N> struct priority_tag : priority_tag < N - 1 > {};
+template<> struct priority_tag<0> {};
+
+
+//////////////////
+// constructors //
+//////////////////
+
+template<value_t> struct external_constructor;
+
+template<>
+struct external_constructor<value_t::boolean>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
+    {
+        j.m_type = value_t::boolean;
+        j.m_value = b;
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::string>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
+    {
+        j.m_type = value_t::string;
+        j.m_value = s;
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::number_float>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
+    {
+        // replace infinity and NAN by null
+        if (not std::isfinite(val))
+        {
+            j = BasicJsonType{};
+        }
+        else
+        {
+            j.m_type = value_t::number_float;
+            j.m_value = val;
+        }
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::number_unsigned>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
+    {
+        j.m_type = value_t::number_unsigned;
+        j.m_value = val;
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::number_integer>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
+    {
+        j.m_type = value_t::number_integer;
+        j.m_value = val;
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::array>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
+    {
+        j.m_type = value_t::array;
+        j.m_value = arr;
+        j.assert_invariant();
+    }
+
+    template<typename BasicJsonType, typename CompatibleArrayType,
+             enable_if_t<not std::is_same<CompatibleArrayType,
+                                          typename BasicJsonType::array_t>::value,
+                         int> = 0>
+    static void construct(BasicJsonType& j, const CompatibleArrayType& arr)
+    {
+        using std::begin;
+        using std::end;
+        j.m_type = value_t::array;
+        j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::object>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
+    {
+        j.m_type = value_t::object;
+        j.m_value = obj;
+        j.assert_invariant();
+    }
+
+    template<typename BasicJsonType, typename CompatibleObjectType,
+             enable_if_t<not std::is_same<CompatibleObjectType,
+                                          typename BasicJsonType::object_t>::value,
+                         int> = 0>
+    static void construct(BasicJsonType& j, const CompatibleObjectType& obj)
+    {
+        using std::begin;
+        using std::end;
+
+        j.m_type = value_t::object;
+        j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
+        j.assert_invariant();
+    }
+};
+
+
+////////////////////////
+// has_/is_ functions //
+////////////////////////
+
 /*!
 @brief Helper to determine whether there's a key_type for T.
 
-Thus helper is used to tell associative containers apart from other containers
+This helper is used to tell associative containers apart from other containers
 such as sequence containers. For instance, `std::map` passes the test as it
 contains a `mapped_type`, whereas `std::vector` fails the test.
 
 @sa http://stackoverflow.com/a/7728728/266378
 @since version 1.0.0, overworked in version 2.0.6
 */
-template<typename T>
-struct has_mapped_type
-{
-  private:
-    template <typename U, typename = typename U::mapped_type>
-    static int detect(U&&);
+#define NLOHMANN_JSON_HAS_HELPER(type)                                        \
+    template<typename T> struct has_##type {                                  \
+    private:                                                                  \
+        template<typename U, typename = typename U::type>                     \
+        static int detect(U &&);                                              \
+        static void detect(...);                                              \
+    public:                                                                   \
+        static constexpr bool value =                                         \
+                std::is_integral<decltype(detect(std::declval<T>()))>::value; \
+    }
 
-    static void detect(...);
-  public:
-    static constexpr bool value =
-        std::is_integral<decltype(detect(std::declval<T>()))>::value;
+NLOHMANN_JSON_HAS_HELPER(mapped_type);
+NLOHMANN_JSON_HAS_HELPER(key_type);
+NLOHMANN_JSON_HAS_HELPER(value_type);
+NLOHMANN_JSON_HAS_HELPER(iterator);
+
+#undef NLOHMANN_JSON_HAS_HELPER
+
+
+template<bool B, class RealType, class CompatibleObjectType>
+struct is_compatible_object_type_impl : std::false_type {};
+
+template<class RealType, class CompatibleObjectType>
+struct is_compatible_object_type_impl<true, RealType, CompatibleObjectType>
+{
+    static constexpr auto value =
+        std::is_constructible<typename RealType::key_type,
+        typename CompatibleObjectType::key_type>::value and
+        std::is_constructible<typename RealType::mapped_type,
+        typename CompatibleObjectType::mapped_type>::value;
 };
 
+template<class BasicJsonType, class CompatibleObjectType>
+struct is_compatible_object_type
+{
+    static auto constexpr value = is_compatible_object_type_impl <
+                                  conjunction<negation<std::is_same<void, CompatibleObjectType>>,
+                                  has_mapped_type<CompatibleObjectType>,
+                                  has_key_type<CompatibleObjectType>>::value,
+                                  typename BasicJsonType::object_t, CompatibleObjectType >::value;
+};
+
+template<typename BasicJsonType, typename T>
+struct is_basic_json_nested_type
+{
+    static auto constexpr value = std::is_same<T, typename BasicJsonType::iterator>::value or
+                                  std::is_same<T, typename BasicJsonType::const_iterator>::value or
+                                  std::is_same<T, typename BasicJsonType::reverse_iterator>::value or
+                                  std::is_same<T, typename BasicJsonType::const_reverse_iterator>::value or
+                                  std::is_same<T, typename BasicJsonType::json_pointer>::value;
+};
+
+template<class BasicJsonType, class CompatibleArrayType>
+struct is_compatible_array_type
+{
+    static auto constexpr value =
+        conjunction<negation<std::is_same<void, CompatibleArrayType>>,
+        negation<is_compatible_object_type<
+        BasicJsonType, CompatibleArrayType>>,
+        negation<std::is_constructible<typename BasicJsonType::string_t,
+        CompatibleArrayType>>,
+        negation<is_basic_json_nested_type<BasicJsonType, CompatibleArrayType>>,
+        has_value_type<CompatibleArrayType>,
+        has_iterator<CompatibleArrayType>>::value;
+};
+
+template<bool, typename, typename>
+struct is_compatible_integer_type_impl : std::false_type {};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType>
+struct is_compatible_integer_type_impl<true, RealIntegerType, CompatibleNumberIntegerType>
+{
+    // is there an assert somewhere on overflows?
+    using RealLimits = std::numeric_limits<RealIntegerType>;
+    using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;
+
+    static constexpr auto value =
+        std::is_constructible<RealIntegerType,
+        CompatibleNumberIntegerType>::value and
+        CompatibleLimits::is_integer and
+        RealLimits::is_signed == CompatibleLimits::is_signed;
+};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType>
+struct is_compatible_integer_type
+{
+    static constexpr auto value =
+        is_compatible_integer_type_impl <
+        std::is_integral<CompatibleNumberIntegerType>::value and
+        not std::is_same<bool, CompatibleNumberIntegerType>::value,
+        RealIntegerType, CompatibleNumberIntegerType > ::value;
+};
+
+
+// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
+template<typename BasicJsonType, typename T>
+struct has_from_json
+{
+  private:
+    // also check the return type of from_json
+    template<typename U, typename = enable_if_t<std::is_same<void, decltype(uncvref_t<U>::from_json(
+                 std::declval<BasicJsonType>(), std::declval<T&>()))>::value>>
+    static int detect(U&&);
+    static void detect(...);
+
+  public:
+    static constexpr bool value = std::is_integral<decltype(
+                                      detect(std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
+};
+
+// This trait checks if JSONSerializer<T>::from_json(json const&) exists
+// this overload is used for non-default-constructible user-defined-types
+template<typename BasicJsonType, typename T>
+struct has_non_default_from_json
+{
+  private:
+    template <
+        typename U,
+        typename = enable_if_t<std::is_same<
+                                   T, decltype(uncvref_t<U>::from_json(std::declval<BasicJsonType>()))>::value >>
+    static int detect(U&&);
+    static void detect(...);
+
+  public:
+    static constexpr bool value = std::is_integral<decltype(detect(
+                                      std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
+};
+
+// This trait checks if BasicJsonType::json_serializer<T>::to_json exists
+template<typename BasicJsonType, typename T>
+struct has_to_json
+{
+  private:
+    template<typename U, typename = decltype(uncvref_t<U>::to_json(
+                 std::declval<BasicJsonType&>(), std::declval<T>()))>
+    static int detect(U&&);
+    static void detect(...);
+
+  public:
+    static constexpr bool value = std::is_integral<decltype(detect(
+                                      std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
+};
+
+
+/////////////
+// to_json //
+/////////////
+
+template<typename BasicJsonType>
+void to_json(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
+{
+    external_constructor<value_t::boolean>::construct(j, b);
 }
 
+template<typename BasicJsonType, typename CompatibleString,
+         enable_if_t<std::is_constructible<typename BasicJsonType::string_t,
+                     CompatibleString>::value, int> = 0>
+void to_json(BasicJsonType& j, const CompatibleString& s)
+{
+    external_constructor<value_t::string>::construct(j, s);
+}
+
+template<typename BasicJsonType, typename FloatType,
+         enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>
+void to_json(BasicJsonType& j, FloatType val) noexcept
+{
+    external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));
+}
+
+template <
+    typename BasicJsonType, typename CompatibleNumberUnsignedType,
+    enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t,
+                CompatibleNumberUnsignedType>::value, int> = 0 >
+void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
+{
+    external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
+}
+
+template <
+    typename BasicJsonType, typename CompatibleNumberIntegerType,
+    enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t,
+                CompatibleNumberIntegerType>::value, int> = 0 >
+void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
+{
+    external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
+}
+
+template<typename BasicJsonType, typename UnscopedEnumType,
+         enable_if_t<is_unscoped_enum<UnscopedEnumType>::value, int> = 0>
+void to_json(BasicJsonType& j, UnscopedEnumType e) noexcept
+{
+    external_constructor<value_t::number_integer>::construct(j, e);
+}
+
+template <
+    typename BasicJsonType, typename CompatibleArrayType,
+    enable_if_t <
+        is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value or
+        std::is_same<typename BasicJsonType::array_t, CompatibleArrayType>::value,
+        int > = 0 >
+void to_json(BasicJsonType& j, const  CompatibleArrayType& arr)
+{
+    external_constructor<value_t::array>::construct(j, arr);
+}
+
+template <
+    typename BasicJsonType, typename CompatibleObjectType,
+    enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value,
+                int> = 0 >
+void to_json(BasicJsonType& j, const  CompatibleObjectType& arr)
+{
+    external_constructor<value_t::object>::construct(j, arr);
+}
+
+
+///////////////
+// from_json //
+///////////////
+
+// overloads for basic_json template parameters
+template<typename BasicJsonType, typename ArithmeticType,
+         enable_if_t<std::is_arithmetic<ArithmeticType>::value and
+                     not std::is_same<ArithmeticType,
+                                      typename BasicJsonType::boolean_t>::value,
+                     int> = 0>
+void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
+{
+    switch (static_cast<value_t>(j))
+    {
+        case value_t::number_unsigned:
+        {
+            val = static_cast<ArithmeticType>(
+                      *j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
+            break;
+        }
+        case value_t::number_integer:
+        {
+            val = static_cast<ArithmeticType>(
+                      *j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
+            break;
+        }
+        case value_t::number_float:
+        {
+            val = static_cast<ArithmeticType>(
+                      *j.template get_ptr<const typename BasicJsonType::number_float_t*>());
+            break;
+        }
+        default:
+        {
+            JSON_THROW(
+                std::domain_error("type must be number, but is " + j.type_name()));
+        }
+    }
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
+{
+    if (not j.is_boolean())
+    {
+        JSON_THROW(std::domain_error("type must be boolean, but is " + j.type_name()));
+    }
+    b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
+{
+    if (not j.is_string())
+    {
+        JSON_THROW(std::domain_error("type must be string, but is " + j.type_name()));
+    }
+    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
+{
+    get_arithmetic_value(j, val);
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
+{
+    get_arithmetic_value(j, val);
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
+{
+    get_arithmetic_value(j, val);
+}
+
+template<typename BasicJsonType, typename UnscopedEnumType,
+         enable_if_t<is_unscoped_enum<UnscopedEnumType>::value, int> = 0>
+void from_json(const BasicJsonType& j, UnscopedEnumType& e)
+{
+    typename std::underlying_type<UnscopedEnumType>::type val = e;
+    get_arithmetic_value(j, val);
+    e = static_cast<UnscopedEnumType>(val);
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::array_t& arr)
+{
+    if (not j.is_array())
+    {
+        JSON_THROW(std::domain_error("type must be array, but is " + j.type_name()));
+    }
+    arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
+}
+
+// forward_list doesn't have an insert method
+template<typename BasicJsonType, typename T, typename Allocator>
+void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
+{
+    // do not perform the check when user wants to retrieve jsons
+    // (except when it's null.. ?)
+    if (j.is_null())
+    {
+        JSON_THROW(std::domain_error("type must be array, but is " + j.type_name()));
+    }
+    if (not std::is_same<T, BasicJsonType>::value)
+    {
+        if (not j.is_array())
+        {
+            JSON_THROW(std::domain_error("type must be array, but is " + j.type_name()));
+        }
+    }
+    for (auto it = j.rbegin(), end = j.rend(); it != end; ++it)
+    {
+        l.push_front(it->template get<T>());
+    }
+}
+
+template<typename BasicJsonType, typename CompatibleArrayType>
+void from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<0>)
+{
+    using std::begin;
+    using std::end;
+
+    std::transform(j.begin(), j.end(),
+                   std::inserter(arr, end(arr)), [](const BasicJsonType & i)
+    {
+        // get<BasicJsonType>() returns *this, this won't call a from_json
+        // method when value_type is BasicJsonType
+        return i.template get<typename CompatibleArrayType::value_type>();
+    });
+}
+
+template<typename BasicJsonType, typename CompatibleArrayType>
+auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<1>)
+-> decltype(
+    arr.reserve(std::declval<typename CompatibleArrayType::size_type>()),
+    void())
+{
+    using std::begin;
+    using std::end;
+
+    arr.reserve(j.size());
+    std::transform(
+        j.begin(), j.end(), std::inserter(arr, end(arr)), [](const BasicJsonType & i)
+    {
+        // get<BasicJsonType>() returns *this, this won't call a from_json
+        // method when value_type is BasicJsonType
+        return i.template get<typename CompatibleArrayType::value_type>();
+    });
+}
+
+template<typename BasicJsonType, typename CompatibleArrayType,
+         enable_if_t<is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value and
+                     not std::is_same<typename BasicJsonType::array_t, CompatibleArrayType>::value, int> = 0>
+void from_json(const BasicJsonType& j, CompatibleArrayType& arr)
+{
+    if (j.is_null())
+    {
+        JSON_THROW(std::domain_error("type must be array, but is " + j.type_name()));
+    }
+
+    // when T == BasicJsonType, do not check if value_t is correct
+    if (not std::is_same<typename CompatibleArrayType::value_type, BasicJsonType>::value)
+    {
+        if (not j.is_array())
+        {
+            JSON_THROW(std::domain_error("type must be array, but is " + j.type_name()));
+        }
+    }
+    from_json_array_impl(j, arr, priority_tag<1> {});
+}
+
+template<typename BasicJsonType, typename CompatibleObjectType,
+         enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value, int> = 0>
+void from_json(const BasicJsonType& j, CompatibleObjectType& obj)
+{
+    if (not j.is_object())
+    {
+        JSON_THROW(std::domain_error("type must be object, but is " + j.type_name()));
+    }
+
+    auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
+    using std::begin;
+    using std::end;
+    // we could avoid the assignment, but this might require a for loop, which
+    // might be less efficient than the container constructor for some
+    // containers (would it?)
+    obj = CompatibleObjectType(begin(*inner_object), end(*inner_object));
+}
+
+// overload for arithmetic types, not chosen for basic_json template arguments
+// (BooleanType, etc..); note: Is it really necessary to provide explicit
+// overloads for boolean_t etc. in case of a custom BooleanType which is not
+// an arithmetic type?
+template<typename BasicJsonType, typename ArithmeticType,
+         enable_if_t <
+             std::is_arithmetic<ArithmeticType>::value and
+             not std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value and
+             not std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value and
+             not std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value and
+             not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
+             int> = 0>
+void from_json(const BasicJsonType& j, ArithmeticType& val)
+{
+    switch (static_cast<value_t>(j))
+    {
+        case value_t::number_unsigned:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
+            break;
+        }
+        case value_t::number_integer:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
+            break;
+        }
+        case value_t::number_float:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
+            break;
+        }
+        case value_t::boolean:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
+            break;
+        }
+        default:
+        {
+            JSON_THROW(std::domain_error("type must be number, but is " + j.type_name()));
+        }
+    }
+}
+
+struct to_json_fn
+{
+  private:
+    template<typename BasicJsonType, typename T>
+    auto call(BasicJsonType& j, T&& val, priority_tag<1>) const noexcept(noexcept(to_json(j, std::forward<T>(val))))
+    -> decltype(to_json(j, std::forward<T>(val)), void())
+    {
+        return to_json(j, std::forward<T>(val));
+    }
+
+    template<typename BasicJsonType, typename T>
+    void call(BasicJsonType&, T&&, priority_tag<0>) const noexcept
+    {
+        static_assert(sizeof(BasicJsonType) == 0,
+                      "could not find to_json() method in T's namespace");
+    }
+
+  public:
+    template<typename BasicJsonType, typename T>
+    void operator()(BasicJsonType& j, T&& val) const
+    noexcept(noexcept(std::declval<to_json_fn>().call(j, std::forward<T>(val), priority_tag<1> {})))
+    {
+        return call(j, std::forward<T>(val), priority_tag<1> {});
+    }
+};
+
+struct from_json_fn
+{
+  private:
+    template<typename BasicJsonType, typename T>
+    auto call(const BasicJsonType& j, T& val, priority_tag<1>) const
+    noexcept(noexcept(from_json(j, val)))
+    -> decltype(from_json(j, val), void())
+    {
+        return from_json(j, val);
+    }
+
+    template<typename BasicJsonType, typename T>
+    void call(const BasicJsonType&, T&, priority_tag<0>) const noexcept
+    {
+        static_assert(sizeof(BasicJsonType) == 0,
+                      "could not find from_json() method in T's namespace");
+    }
+
+  public:
+    template<typename BasicJsonType, typename T>
+    void operator()(const BasicJsonType& j, T& val) const
+    noexcept(noexcept(std::declval<from_json_fn>().call(j, val, priority_tag<1> {})))
+    {
+        return call(j, val, priority_tag<1> {});
+    }
+};
+
+// taken from ranges-v3
+template<typename T>
+struct static_const
+{
+    static constexpr T value{};
+};
+
+template<typename T>
+constexpr T static_const<T>::value;
+} // namespace detail
+
+
+/// namespace to hold default `to_json` / `from_json` functions
+namespace
+{
+constexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value;
+constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value;
+}
+
+
+/*!
+@brief default JSONSerializer template argument
+
+This serializer ignores the template arguments and uses ADL
+([argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adl))
+for serialization.
+*/
+template<typename = void, typename = void>
+struct adl_serializer
+{
+    /*!
+    @brief convert a JSON value to any value type
+
+    This function is usually called by the `get()` function of the
+    @ref basic_json class (either explicit or via conversion operators).
+
+    @param[in] j         JSON value to read from
+    @param[in, out] val  value to write to
+    */
+    template<typename BasicJsonType, typename ValueType>
+    static void from_json(BasicJsonType&& j, ValueType& val) noexcept(
+        noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))
+    {
+        ::nlohmann::from_json(std::forward<BasicJsonType>(j), val);
+    }
+
+    /*!
+    @brief convert any value type to a JSON value
+
+    This function is usually called by the constructors of the @ref basic_json
+    class.
+
+    @param[in, out] j  JSON value to write to
+    @param[in] val     value to read from
+    */
+    template<typename BasicJsonType, typename ValueType>
+    static void to_json(BasicJsonType& j, ValueType&& val) noexcept(
+        noexcept(::nlohmann::to_json(j, std::forward<ValueType>(val))))
+    {
+        ::nlohmann::to_json(j, std::forward<ValueType>(val));
+    }
+};
+
+
 /*!
 @brief a class to store JSON values
 
@@ -149,11 +962,14 @@
 default; will be used in @ref number_float_t)
 @tparam AllocatorType type of the allocator to use (`std::allocator` by
 default)
+@tparam JSONSerializer the serializer to resolve internal calls to `to_json()`
+and `from_json()` (@ref adl_serializer by default)
 
 @requirement The class satisfies the following concept requirements:
 - Basic
  - [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible):
-   JSON values can be default constructed. The result will be a JSON null value.
+   JSON values can be default constructed. The result will be a JSON null
+   value.
  - [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible):
    A JSON value can be constructed from an rvalue argument.
  - [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible):
@@ -168,8 +984,8 @@
  - [StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType):
    JSON values have
    [standard layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout):
-   All non-static data members are private and standard layout types, the class
-   has no virtual functions or (virtual) base classes.
+   All non-static data members are private and standard layout types, the
+   class has no virtual functions or (virtual) base classes.
 - Library-wide
  - [EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable):
    JSON values can be compared with `==`, see @ref
@@ -216,21 +1032,26 @@
     class NumberIntegerType = std::int64_t,
     class NumberUnsignedType = std::uint64_t,
     class NumberFloatType = double,
-    template<typename U> class AllocatorType = std::allocator
+    template<typename U> class AllocatorType = std::allocator,
+    template<typename T, typename SFINAE = void> class JSONSerializer = adl_serializer
     >
 class basic_json
 {
   private:
+    template<detail::value_t> friend struct detail::external_constructor;
     /// workaround type for MSVC
     using basic_json_t = basic_json<ObjectType, ArrayType, StringType,
           BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType,
-          AllocatorType>;
+          AllocatorType, JSONSerializer>;
 
   public:
+    using value_t = detail::value_t;
     // forward declarations
     template<typename U> class iter_impl;
     template<typename Base> class json_reverse_iterator;
     class json_pointer;
+    template<typename T, typename SFINAE>
+    using json_serializer = JSONSerializer<T, SFINAE>;
 
     /////////////////////
     // container types //
@@ -282,6 +1103,84 @@
         return allocator_type();
     }
 
+    /*!
+    @brief returns version information on the library
+
+    This function returns a JSON object with infiormation about the library,
+    including the version number and information on the platform and compiler.
+
+    @return JSON object holding version information
+    key         | description
+    ----------- | ---------------
+    `compiler`  | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version).
+    `copyright` | The copyright line for the library as string.
+    `name`      | The name of the library as string.
+    `platform`  | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`.
+    `url`       | The URL of the project as string.
+    `version`   | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string).
+
+    @liveexample{The following code shows an example output of the `meta()`
+    function.,meta}
+
+    @complexity Constant.
+
+    @since 2.1.0
+    */
+    static basic_json meta()
+    {
+        basic_json result;
+
+        result["copyright"] = "(C) 2013-2017 Niels Lohmann";
+        result["name"] = "JSON for Modern C++";
+        result["url"] = "https://github.com/nlohmann/json";
+        result["version"] =
+        {
+            {"string", "2.1.0"},
+            {"major", 2},
+            {"minor", 1},
+            {"patch", 0},
+        };
+
+#ifdef _WIN32
+        result["platform"] = "win32";
+#elif defined __linux__
+        result["platform"] = "linux";
+#elif defined __APPLE__
+        result["platform"] = "apple";
+#elif defined __unix__
+        result["platform"] = "unix";
+#else
+        result["platform"] = "unknown";
+#endif
+
+#if defined(__clang__)
+        result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}};
+#elif defined(__ICC) || defined(__INTEL_COMPILER)
+        result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}};
+#elif defined(__GNUC__) || defined(__GNUG__)
+        result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}};
+#elif defined(__HP_cc) || defined(__HP_aCC)
+        result["compiler"] = "hp"
+#elif defined(__IBMCPP__)
+        result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}};
+#elif defined(_MSC_VER)
+        result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}};
+#elif defined(__PGI)
+        result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}};
+#elif defined(__SUNPRO_CC)
+        result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}};
+#else
+        result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}};
+#endif
+
+#ifdef __cplusplus
+        result["compiler"]["c++"] = std::to_string(__cplusplus);
+#else
+        result["compiler"]["c++"] = "unknown";
+#endif
+        return result;
+    }
+
 
     ///////////////////////////
     // JSON value data types //
@@ -449,6 +1348,12 @@
     std::string
     @endcode
 
+    #### Encoding
+
+    Strings are stored in UTF-8 encoding. Therefore, functions like
+    `std::string::size()` or `std::string::length()` return the number of
+    bytes in the string rather than the number of characters or glyphs.
+
     #### String comparison
 
     [RFC 7159](http://rfc7159.net/rfc7159) states:
@@ -713,47 +1618,6 @@
 
     /// @}
 
-
-    ///////////////////////////
-    // JSON type enumeration //
-    ///////////////////////////
-
-    /*!
-    @brief the JSON type enumeration
-
-    This enumeration collects the different JSON types. It is internally used
-    to distinguish the stored values, and the functions @ref is_null(), @ref
-    is_object(), @ref is_array(), @ref is_string(), @ref is_boolean(), @ref
-    is_number() (with @ref is_number_integer(), @ref is_number_unsigned(), and
-    @ref is_number_float()), @ref is_discarded(), @ref is_primitive(), and
-    @ref is_structured() rely on it.
-
-    @note There are three enumeration entries (number_integer,
-    number_unsigned, and number_float), because the library distinguishes
-    these three types for numbers: @ref number_unsigned_t is used for unsigned
-    integers, @ref number_integer_t is used for signed integers, and @ref
-    number_float_t is used for floating-point numbers or to approximate
-    integers which do not fit in the limits of their respective type.
-
-    @sa @ref basic_json(const value_t value_type) -- create a JSON value with
-    the default value for a given type
-
-    @since version 1.0.0
-    */
-    enum class value_t : uint8_t
-    {
-        null,            ///< null value
-        object,          ///< object (unordered set of name/value pairs)
-        array,           ///< array (ordered collection of values)
-        string,          ///< string value
-        boolean,         ///< boolean value
-        number_integer,  ///< number value (signed integer)
-        number_unsigned, ///< number value (unsigned integer)
-        number_float,    ///< number value (floating-point)
-        discarded        ///< discarded by the the parser callback function
-    };
-
-
   private:
 
     /// helper for exception-safe object creation
@@ -767,7 +1631,7 @@
         };
         std::unique_ptr<T, decltype(deleter)> object(alloc.allocate(1), deleter);
         alloc.construct(object.get(), std::forward<Args>(args)...);
-        assert(object.get() != nullptr);
+        assert(object != nullptr);
         return object.release();
     }
 
@@ -882,7 +1746,7 @@
                 {
                     if (t == value_t::null)
                     {
-                        throw std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21 2.0.10"); // LCOV_EXCL_LINE
+                        JSON_THROW(std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21 2.1.0")); // LCOV_EXCL_LINE
                     }
                     break;
                 }
@@ -1046,18 +1910,6 @@
     @liveexample{The following code shows the constructor for different @ref
     value_t values,basic_json__value_t}
 
-    @sa @ref basic_json(std::nullptr_t) -- create a `null` value
-    @sa @ref basic_json(boolean_t value) -- create a boolean value
-    @sa @ref basic_json(const string_t&) -- create a string value
-    @sa @ref basic_json(const object_t&) -- create a object value
-    @sa @ref basic_json(const array_t&) -- create a array value
-    @sa @ref basic_json(const number_float_t) -- create a number
-    (floating-point) value
-    @sa @ref basic_json(const number_integer_t) -- create a number (integer)
-    value
-    @sa @ref basic_json(const number_unsigned_t) -- create a number (unsigned)
-    value
-
     @since version 1.0.0
     */
     basic_json(const value_t value_type)
@@ -1091,473 +1943,69 @@
     }
 
     /*!
-    @brief create an object (explicit)
+    @brief create a JSON value
 
-    Create an object JSON value with a given content.
+    This is a "catch all" constructor for all compatible JSON types; that is,
+    types for which a `to_json()` method exsits. The constructor forwards the
+    parameter @a val to that method (to `json_serializer<U>::to_json` method
+    with `U = uncvref_t<CompatibleType>`, to be exact).
 
-    @param[in] val  a value for the object
+    Template type @a CompatibleType includes, but is not limited to, the
+    following types:
+    - **arrays**: @ref array_t and all kinds of compatible containers such as
+      `std::vector`, `std::deque`, `std::list`, `std::forward_list`,
+      `std::array`, `std::set`, `std::unordered_set`, `std::multiset`, and
+      `unordered_multiset` with a `value_type` from which a @ref basic_json
+      value can be constructed.
+    - **objects**: @ref object_t and all kinds of compatible associative
+      containers such as `std::map`, `std::unordered_map`, `std::multimap`,
+      and `std::unordered_multimap` with a `key_type` compatible to
+      @ref string_t and a `value_type` from which a @ref basic_json value can
+      be constructed.
+    - **strings**: @ref string_t, string literals, and all compatible string
+      containers can be used.
+    - **numbers**: @ref number_integer_t, @ref number_unsigned_t,
+      @ref number_float_t, and all convertible number types such as `int`,
+      `size_t`, `int64_t`, `float` or `double` can be used.
+    - **boolean**: @ref boolean_t / `bool` can be used.
 
-    @complexity Linear in the size of the passed @a val.
+    See the examples below.
 
-    @throw std::bad_alloc if allocation for object value fails
+    @tparam CompatibleType a type such that:
+    - @a CompatibleType is not derived from `std::istream`,
+    - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move
+         constructors),
+    - @a CompatibleType is not a @ref basic_json nested type (e.g.,
+         @ref json_pointer, @ref iterator, etc ...)
+    - @ref @ref json_serializer<U> has a
+         `to_json(basic_json_t&, CompatibleType&&)` method
 
-    @liveexample{The following code shows the constructor with an @ref
-    object_t parameter.,basic_json__object_t}
+    @tparam U = `uncvref_t<CompatibleType>`
 
-    @sa @ref basic_json(const CompatibleObjectType&) -- create an object value
-    from a compatible STL container
+    @param[in] val the value to be forwarded
 
-    @since version 1.0.0
-    */
-    basic_json(const object_t& val)
-        : m_type(value_t::object), m_value(val)
-    {
-        assert_invariant();
-    }
+    @complexity Usually linear in the size of the passed @a val, also
+                depending on the implementation of the called `to_json()`
+                method.
 
-    /*!
-    @brief create an object (implicit)
-
-    Create an object JSON value with a given content. This constructor allows
-    any type @a CompatibleObjectType that can be used to construct values of
-    type @ref object_t.
-
-    @tparam CompatibleObjectType An object type whose `key_type` and
-    `value_type` is compatible to @ref object_t. Examples include `std::map`,
-    `std::unordered_map`, `std::multimap`, and `std::unordered_multimap` with
-    a `key_type` of `std::string`, and a `value_type` from which a @ref
-    basic_json value can be constructed.
-
-    @param[in] val  a value for the object
-
-    @complexity Linear in the size of the passed @a val.
-
-    @throw std::bad_alloc if allocation for object value fails
+    @throw what `json_serializer<U>::to_json()` throws
 
     @liveexample{The following code shows the constructor with several
-    compatible object type parameters.,basic_json__CompatibleObjectType}
+    compatible types.,basic_json__CompatibleType}
 
-    @sa @ref basic_json(const object_t&) -- create an object value
-
-    @since version 1.0.0
+    @since version 2.1.0
     */
-    template<class CompatibleObjectType, typename std::enable_if<
-                 std::is_constructible<typename object_t::key_type, typename CompatibleObjectType::key_type>::value and
-                 std::is_constructible<basic_json, typename CompatibleObjectType::mapped_type>::value, int>::type = 0>
-    basic_json(const CompatibleObjectType& val)
-        : m_type(value_t::object)
+    template<typename CompatibleType, typename U = detail::uncvref_t<CompatibleType>,
+             detail::enable_if_t<not std::is_base_of<std::istream, U>::value and
+                                 not std::is_same<U, basic_json_t>::value and
+                                 not detail::is_basic_json_nested_type<
+                                     basic_json_t, U>::value and
+                                 detail::has_to_json<basic_json, U>::value,
+                                 int> = 0>
+    basic_json(CompatibleType && val) noexcept(noexcept(JSONSerializer<U>::to_json(
+                std::declval<basic_json_t&>(), std::forward<CompatibleType>(val))))
     {
-        using std::begin;
-        using std::end;
-        m_value.object = create<object_t>(begin(val), end(val));
-        assert_invariant();
-    }
-
-    /*!
-    @brief create an array (explicit)
-
-    Create an array JSON value with a given content.
-
-    @param[in] val  a value for the array
-
-    @complexity Linear in the size of the passed @a val.
-
-    @throw std::bad_alloc if allocation for array value fails
-
-    @liveexample{The following code shows the constructor with an @ref array_t
-    parameter.,basic_json__array_t}
-
-    @sa @ref basic_json(const CompatibleArrayType&) -- create an array value
-    from a compatible STL containers
-
-    @since version 1.0.0
-    */
-    basic_json(const array_t& val)
-        : m_type(value_t::array), m_value(val)
-    {
-        assert_invariant();
-    }
-
-    /*!
-    @brief create an array (implicit)
-
-    Create an array JSON value with a given content. This constructor allows
-    any type @a CompatibleArrayType that can be used to construct values of
-    type @ref array_t.
-
-    @tparam CompatibleArrayType An object type whose `value_type` is
-    compatible to @ref array_t. Examples include `std::vector`, `std::deque`,
-    `std::list`, `std::forward_list`, `std::array`, `std::set`,
-    `std::unordered_set`, `std::multiset`, and `unordered_multiset` with a
-    `value_type` from which a @ref basic_json value can be constructed.
-
-    @param[in] val  a value for the array
-
-    @complexity Linear in the size of the passed @a val.
-
-    @throw std::bad_alloc if allocation for array value fails
-
-    @liveexample{The following code shows the constructor with several
-    compatible array type parameters.,basic_json__CompatibleArrayType}
-
-    @sa @ref basic_json(const array_t&) -- create an array value
-
-    @since version 1.0.0
-    */
-    template<class CompatibleArrayType, typename std::enable_if<
-                 not std::is_same<CompatibleArrayType, typename basic_json_t::iterator>::value and
-                 not std::is_same<CompatibleArrayType, typename basic_json_t::const_iterator>::value and
-                 not std::is_same<CompatibleArrayType, typename basic_json_t::reverse_iterator>::value and
-                 not std::is_same<CompatibleArrayType, typename basic_json_t::const_reverse_iterator>::value and
-                 not std::is_same<CompatibleArrayType, typename array_t::iterator>::value and
-                 not std::is_same<CompatibleArrayType, typename array_t::const_iterator>::value and
-                 std::is_constructible<basic_json, typename CompatibleArrayType::value_type>::value, int>::type = 0>
-    basic_json(const CompatibleArrayType& val)
-        : m_type(value_t::array)
-    {
-        using std::begin;
-        using std::end;
-        m_value.array = create<array_t>(begin(val), end(val));
-        assert_invariant();
-    }
-
-    /*!
-    @brief create a string (explicit)
-
-    Create an string JSON value with a given content.
-
-    @param[in] val  a value for the string
-
-    @complexity Linear in the size of the passed @a val.
-
-    @throw std::bad_alloc if allocation for string value fails
-
-    @liveexample{The following code shows the constructor with an @ref
-    string_t parameter.,basic_json__string_t}
-
-    @sa @ref basic_json(const typename string_t::value_type*) -- create a
-    string value from a character pointer
-    @sa @ref basic_json(const CompatibleStringType&) -- create a string value
-    from a compatible string container
-
-    @since version 1.0.0
-    */
-    basic_json(const string_t& val)
-        : m_type(value_t::string), m_value(val)
-    {
-        assert_invariant();
-    }
-
-    /*!
-    @brief create a string (explicit)
-
-    Create a string JSON value with a given content.
-
-    @param[in] val  a literal value for the string
-
-    @complexity Linear in the size of the passed @a val.
-
-    @throw std::bad_alloc if allocation for string value fails
-
-    @liveexample{The following code shows the constructor with string literal
-    parameter.,basic_json__string_t_value_type}
-
-    @sa @ref basic_json(const string_t&) -- create a string value
-    @sa @ref basic_json(const CompatibleStringType&) -- create a string value
-    from a compatible string container
-
-    @since version 1.0.0
-    */
-    basic_json(const typename string_t::value_type* val)
-        : basic_json(string_t(val))
-    {
-        assert_invariant();
-    }
-
-    /*!
-    @brief create a string (implicit)
-
-    Create a string JSON value with a given content.
-
-    @param[in] val  a value for the string
-
-    @tparam CompatibleStringType an string type which is compatible to @ref
-    string_t, for instance `std::string`.
-
-    @complexity Linear in the size of the passed @a val.
-
-    @throw std::bad_alloc if allocation for string value fails
-
-    @liveexample{The following code shows the construction of a string value
-    from a compatible type.,basic_json__CompatibleStringType}
-
-    @sa @ref basic_json(const string_t&) -- create a string value
-    @sa @ref basic_json(const typename string_t::value_type*) -- create a
-    string value from a character pointer
-
-    @since version 1.0.0
-    */
-    template<class CompatibleStringType, typename std::enable_if<
-                 std::is_constructible<string_t, CompatibleStringType>::value, int>::type = 0>
-    basic_json(const CompatibleStringType& val)
-        : basic_json(string_t(val))
-    {
-        assert_invariant();
-    }
-
-    /*!
-    @brief create a boolean (explicit)
-
-    Creates a JSON boolean type from a given value.
-
-    @param[in] val  a boolean value to store
-
-    @complexity Constant.
-
-    @liveexample{The example below demonstrates boolean
-    values.,basic_json__boolean_t}
-
-    @since version 1.0.0
-    */
-    basic_json(boolean_t val) noexcept
-        : m_type(value_t::boolean), m_value(val)
-    {
-        assert_invariant();
-    }
-
-    /*!
-    @brief create an integer number (explicit)
-
-    Create an integer number JSON value with a given content.
-
-    @tparam T A helper type to remove this function via SFINAE in case @ref
-    number_integer_t is the same as `int`. In this case, this constructor
-    would have the same signature as @ref basic_json(const int value). Note
-    the helper type @a T is not visible in this constructor's interface.
-
-    @param[in] val  an integer to create a JSON number from
-
-    @complexity Constant.
-
-    @liveexample{The example below shows the construction of an integer
-    number value.,basic_json__number_integer_t}
-
-    @sa @ref basic_json(const int) -- create a number value (integer)
-    @sa @ref basic_json(const CompatibleNumberIntegerType) -- create a number
-    value (integer) from a compatible number type
-
-    @since version 1.0.0
-    */
-    template<typename T, typename std::enable_if<
-                 not (std::is_same<T, int>::value) and
-                 std::is_same<T, number_integer_t>::value, int>::type = 0>
-    basic_json(const number_integer_t val) noexcept
-        : m_type(value_t::number_integer), m_value(val)
-    {
-        assert_invariant();
-    }
-
-    /*!
-    @brief create an integer number from an enum type (explicit)
-
-    Create an integer number JSON value with a given content.
-
-    @param[in] val  an integer to create a JSON number from
-
-    @note This constructor allows to pass enums directly to a constructor. As
-    C++ has no way of specifying the type of an anonymous enum explicitly, we
-    can only rely on the fact that such values implicitly convert to int. As
-    int may already be the same type of number_integer_t, we may need to
-    switch off the constructor @ref basic_json(const number_integer_t).
-
-    @complexity Constant.
-
-    @liveexample{The example below shows the construction of an integer
-    number value from an anonymous enum.,basic_json__const_int}
-
-    @sa @ref basic_json(const number_integer_t) -- create a number value
-    (integer)
-    @sa @ref basic_json(const CompatibleNumberIntegerType) -- create a number
-    value (integer) from a compatible number type
-
-    @since version 1.0.0
-    */
-    basic_json(const int val) noexcept
-        : m_type(value_t::number_integer),
-          m_value(static_cast<number_integer_t>(val))
-    {
-        assert_invariant();
-    }
-
-    /*!
-    @brief create an integer number (implicit)
-
-    Create an integer number JSON value with a given content. This constructor
-    allows any type @a CompatibleNumberIntegerType that can be used to
-    construct values of type @ref number_integer_t.
-
-    @tparam CompatibleNumberIntegerType An integer type which is compatible to
-    @ref number_integer_t. Examples include the types `int`, `int32_t`,
-    `long`, and `short`.
-
-    @param[in] val  an integer to create a JSON number from
-
-    @complexity Constant.
-
-    @liveexample{The example below shows the construction of several integer
-    number values from compatible
-    types.,basic_json__CompatibleIntegerNumberType}
-
-    @sa @ref basic_json(const number_integer_t) -- create a number value
-    (integer)
-    @sa @ref basic_json(const int) -- create a number value (integer)
-
-    @since version 1.0.0
-    */
-    template<typename CompatibleNumberIntegerType, typename std::enable_if<
-                 std::is_constructible<number_integer_t, CompatibleNumberIntegerType>::value and
-                 std::numeric_limits<CompatibleNumberIntegerType>::is_integer and
-                 std::numeric_limits<CompatibleNumberIntegerType>::is_signed,
-                 CompatibleNumberIntegerType>::type = 0>
-    basic_json(const CompatibleNumberIntegerType val) noexcept
-        : m_type(value_t::number_integer),
-          m_value(static_cast<number_integer_t>(val))
-    {
-        assert_invariant();
-    }
-
-    /*!
-    @brief create an unsigned integer number (explicit)
-
-    Create an unsigned integer number JSON value with a given content.
-
-    @tparam T  helper type to compare number_unsigned_t and unsigned int (not
-    visible in) the interface.
-
-    @param[in] val  an integer to create a JSON number from
-
-    @complexity Constant.
-
-    @sa @ref basic_json(const CompatibleNumberUnsignedType) -- create a number
-    value (unsigned integer) from a compatible number type
-
-    @since version 2.0.0
-    */
-    template<typename T, typename std::enable_if<
-                 not (std::is_same<T, int>::value) and
-                 std::is_same<T, number_unsigned_t>::value, int>::type = 0>
-    basic_json(const number_unsigned_t val) noexcept
-        : m_type(value_t::number_unsigned), m_value(val)
-    {
-        assert_invariant();
-    }
-
-    /*!
-    @brief create an unsigned number (implicit)
-
-    Create an unsigned number JSON value with a given content. This
-    constructor allows any type @a CompatibleNumberUnsignedType that can be
-    used to construct values of type @ref number_unsigned_t.
-
-    @tparam CompatibleNumberUnsignedType An integer type which is compatible
-    to @ref number_unsigned_t. Examples may include the types `unsigned int`,
-    `uint32_t`, or `unsigned short`.
-
-    @param[in] val  an unsigned integer to create a JSON number from
-
-    @complexity Constant.
-
-    @sa @ref basic_json(const number_unsigned_t) -- create a number value
-    (unsigned)
-
-    @since version 2.0.0
-    */
-    template<typename CompatibleNumberUnsignedType, typename std::enable_if <
-                 std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and
-                 std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and
-                 not std::numeric_limits<CompatibleNumberUnsignedType>::is_signed,
-                 CompatibleNumberUnsignedType>::type = 0>
-    basic_json(const CompatibleNumberUnsignedType val) noexcept
-        : m_type(value_t::number_unsigned),
-          m_value(static_cast<number_unsigned_t>(val))
-    {
-        assert_invariant();
-    }
-
-    /*!
-    @brief create a floating-point number (explicit)
-
-    Create a floating-point number JSON value with a given content.
-
-    @param[in] val  a floating-point value to create a JSON number from
-
-    @note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6
-    disallows NaN values:
-    > Numeric values that cannot be represented in the grammar below (such as
-    > Infinity and NaN) are not permitted.
-    In case the parameter @a val is not a number, a JSON null value is created
-    instead.
-
-    @complexity Constant.
-
-    @liveexample{The following example creates several floating-point
-    values.,basic_json__number_float_t}
-
-    @sa @ref basic_json(const CompatibleNumberFloatType) -- create a number
-    value (floating-point) from a compatible number type
-
-    @since version 1.0.0
-    */
-    basic_json(const number_float_t val) noexcept
-        : m_type(value_t::number_float), m_value(val)
-    {
-        // replace infinity and NAN by null
-        if (not std::isfinite(val))
-        {
-            m_type = value_t::null;
-            m_value = json_value();
-        }
-
-        assert_invariant();
-    }
-
-    /*!
-    @brief create an floating-point number (implicit)
-
-    Create an floating-point number JSON value with a given content. This
-    constructor allows any type @a CompatibleNumberFloatType that can be used
-    to construct values of type @ref number_float_t.
-
-    @tparam CompatibleNumberFloatType A floating-point type which is
-    compatible to @ref number_float_t. Examples may include the types `float`
-    or `double`.
-
-    @param[in] val  a floating-point to create a JSON number from
-
-    @note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6
-    disallows NaN values:
-    > Numeric values that cannot be represented in the grammar below (such as
-    > Infinity and NaN) are not permitted.
-    In case the parameter @a val is not a number, a JSON null value is
-    created instead.
-
-    @complexity Constant.
-
-    @liveexample{The example below shows the construction of several
-    floating-point number values from compatible
-    types.,basic_json__CompatibleNumberFloatType}
-
-    @sa @ref basic_json(const number_float_t) -- create a number value
-    (floating-point)
-
-    @since version 1.0.0
-    */
-    template<typename CompatibleNumberFloatType, typename = typename std::enable_if<
-                 std::is_constructible<number_float_t, CompatibleNumberFloatType>::value and
-                 std::is_floating_point<CompatibleNumberFloatType>::value>::type>
-    basic_json(const CompatibleNumberFloatType val) noexcept
-        : basic_json(number_float_t(val))
-    {
+        JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));
         assert_invariant();
     }
 
@@ -1654,7 +2102,7 @@
             // if object is wanted but impossible, throw an exception
             if (manual_type == value_t::object and not is_an_object)
             {
-                throw std::domain_error("cannot create object from initializer list");
+                JSON_THROW(std::domain_error("cannot create object from initializer list"));
             }
         }
 
@@ -1832,7 +2280,7 @@
         // make sure iterator fits the current value
         if (first.m_object != last.m_object)
         {
-            throw std::domain_error("iterators are not compatible");
+            JSON_THROW(std::domain_error("iterators are not compatible"));
         }
 
         // copy type from first iterator
@@ -1849,7 +2297,7 @@
             {
                 if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end())
                 {
-                    throw std::out_of_range("iterators out of range");
+                    JSON_THROW(std::out_of_range("iterators out of range"));
                 }
                 break;
             }
@@ -1894,19 +2342,21 @@
 
             case value_t::object:
             {
-                m_value.object = create<object_t>(first.m_it.object_iterator, last.m_it.object_iterator);
+                m_value.object = create<object_t>(first.m_it.object_iterator,
+                                                  last.m_it.object_iterator);
                 break;
             }
 
             case value_t::array:
             {
-                m_value.array = create<array_t>(first.m_it.array_iterator, last.m_it.array_iterator);
+                m_value.array = create<array_t>(first.m_it.array_iterator,
+                                                last.m_it.array_iterator);
                 break;
             }
 
             default:
             {
-                throw std::domain_error("cannot use construct with iterators from " + first.m_object->type_name());
+                JSON_THROW(std::domain_error("cannot use construct with iterators from " + first.m_object->type_name()));
             }
         }
 
@@ -2579,244 +3029,99 @@
     // value access //
     //////////////////
 
-    /// get an object (explicit)
-    template<class T, typename std::enable_if<
-                 std::is_convertible<typename object_t::key_type, typename T::key_type>::value and
-                 std::is_convertible<basic_json_t, typename T::mapped_type>::value, int>::type = 0>
-    T get_impl(T*) const
-    {
-        if (is_object())
-        {
-            return T(m_value.object->begin(), m_value.object->end());
-        }
-        else
-        {
-            throw std::domain_error("type must be object, but is " + type_name());
-        }
-    }
-
-    /// get an object (explicit)
-    object_t get_impl(object_t*) const
-    {
-        if (is_object())
-        {
-            return *(m_value.object);
-        }
-        else
-        {
-            throw std::domain_error("type must be object, but is " + type_name());
-        }
-    }
-
-    /// get an array (explicit)
-    template<class T, typename std::enable_if<
-                 std::is_convertible<basic_json_t, typename T::value_type>::value and
-                 not std::is_same<basic_json_t, typename T::value_type>::value and
-                 not std::is_arithmetic<T>::value and
-                 not std::is_convertible<std::string, T>::value and
-                 not has_mapped_type<T>::value, int>::type = 0>
-    T get_impl(T*) const
-    {
-        if (is_array())
-        {
-            T to_vector;
-            std::transform(m_value.array->begin(), m_value.array->end(),
-                           std::inserter(to_vector, to_vector.end()), [](basic_json i)
-            {
-                return i.get<typename T::value_type>();
-            });
-            return to_vector;
-        }
-        else
-        {
-            throw std::domain_error("type must be array, but is " + type_name());
-        }
-    }
-
-    /// get an array (explicit)
-    template<class T, typename std::enable_if<
-                 std::is_convertible<basic_json_t, T>::value and
-                 not std::is_same<basic_json_t, T>::value, int>::type = 0>
-    std::vector<T> get_impl(std::vector<T>*) const
-    {
-        if (is_array())
-        {
-            std::vector<T> to_vector;
-            to_vector.reserve(m_value.array->size());
-            std::transform(m_value.array->begin(), m_value.array->end(),
-                           std::inserter(to_vector, to_vector.end()), [](basic_json i)
-            {
-                return i.get<T>();
-            });
-            return to_vector;
-        }
-        else
-        {
-            throw std::domain_error("type must be array, but is " + type_name());
-        }
-    }
-
-    /// get an array (explicit)
-    template<class T, typename std::enable_if<
-                 std::is_same<basic_json, typename T::value_type>::value and
-                 not has_mapped_type<T>::value, int>::type = 0>
-    T get_impl(T*) const
-    {
-        if (is_array())
-        {
-            return T(m_value.array->begin(), m_value.array->end());
-        }
-        else
-        {
-            throw std::domain_error("type must be array, but is " + type_name());
-        }
-    }
-
-    /// get an array (explicit)
-    array_t get_impl(array_t*) const
-    {
-        if (is_array())
-        {
-            return *(m_value.array);
-        }
-        else
-        {
-            throw std::domain_error("type must be array, but is " + type_name());
-        }
-    }
-
-    /// get a string (explicit)
-    template<typename T, typename std::enable_if<
-                 std::is_convertible<string_t, T>::value, int>::type = 0>
-    T get_impl(T*) const
-    {
-        if (is_string())
-        {
-            return *m_value.string;
-        }
-        else
-        {
-            throw std::domain_error("type must be string, but is " + type_name());
-        }
-    }
-
-    /// get a number (explicit)
-    template<typename T, typename std::enable_if<
-                 std::is_arithmetic<T>::value, int>::type = 0>
-    T get_impl(T*) const
-    {
-        switch (m_type)
-        {
-            case value_t::number_integer:
-            {
-                return static_cast<T>(m_value.number_integer);
-            }
-
-            case value_t::number_unsigned:
-            {
-                return static_cast<T>(m_value.number_unsigned);
-            }
-
-            case value_t::number_float:
-            {
-                return static_cast<T>(m_value.number_float);
-            }
-
-            default:
-            {
-                throw std::domain_error("type must be number, but is " + type_name());
-            }
-        }
-    }
-
     /// get a boolean (explicit)
-    constexpr boolean_t get_impl(boolean_t*) const
+    boolean_t get_impl(boolean_t* /*unused*/) const
     {
-        return is_boolean()
-               ? m_value.boolean
-               : throw std::domain_error("type must be boolean, but is " + type_name());
+        if (is_boolean())
+        {
+            return m_value.boolean;
+        }
+        else
+        {
+            JSON_THROW(std::domain_error("type must be boolean, but is " + type_name()));
+        }
     }
 
     /// get a pointer to the value (object)
-    object_t* get_impl_ptr(object_t*) noexcept
+    object_t* get_impl_ptr(object_t* /*unused*/) noexcept
     {
         return is_object() ? m_value.object : nullptr;
     }
 
     /// get a pointer to the value (object)
-    constexpr const object_t* get_impl_ptr(const object_t*) const noexcept
+    constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept
     {
         return is_object() ? m_value.object : nullptr;
     }
 
     /// get a pointer to the value (array)
-    array_t* get_impl_ptr(array_t*) noexcept
+    array_t* get_impl_ptr(array_t* /*unused*/) noexcept
     {
         return is_array() ? m_value.array : nullptr;
     }
 
     /// get a pointer to the value (array)
-    constexpr const array_t* get_impl_ptr(const array_t*) const noexcept
+    constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept
     {
         return is_array() ? m_value.array : nullptr;
     }
 
     /// get a pointer to the value (string)
-    string_t* get_impl_ptr(string_t*) noexcept
+    string_t* get_impl_ptr(string_t* /*unused*/) noexcept
     {
         return is_string() ? m_value.string : nullptr;
     }
 
     /// get a pointer to the value (string)
-    constexpr const string_t* get_impl_ptr(const string_t*) const noexcept
+    constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept
     {
         return is_string() ? m_value.string : nullptr;
     }
 
     /// get a pointer to the value (boolean)
-    boolean_t* get_impl_ptr(boolean_t*) noexcept
+    boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept
     {
         return is_boolean() ? &m_value.boolean : nullptr;
     }
 
     /// get a pointer to the value (boolean)
-    constexpr const boolean_t* get_impl_ptr(const boolean_t*) const noexcept
+    constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept
     {
         return is_boolean() ? &m_value.boolean : nullptr;
     }
 
     /// get a pointer to the value (integer number)
-    number_integer_t* get_impl_ptr(number_integer_t*) noexcept
+    number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept
     {
         return is_number_integer() ? &m_value.number_integer : nullptr;
     }
 
     /// get a pointer to the value (integer number)
-    constexpr const number_integer_t* get_impl_ptr(const number_integer_t*) const noexcept
+    constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept
     {
         return is_number_integer() ? &m_value.number_integer : nullptr;
     }
 
     /// get a pointer to the value (unsigned number)
-    number_unsigned_t* get_impl_ptr(number_unsigned_t*) noexcept
+    number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept
     {
         return is_number_unsigned() ? &m_value.number_unsigned : nullptr;
     }
 
     /// get a pointer to the value (unsigned number)
-    constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t*) const noexcept
+    constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept
     {
         return is_number_unsigned() ? &m_value.number_unsigned : nullptr;
     }
 
     /// get a pointer to the value (floating-point number)
-    number_float_t* get_impl_ptr(number_float_t*) noexcept
+    number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept
     {
         return is_number_float() ? &m_value.number_float : nullptr;
     }
 
     /// get a pointer to the value (floating-point number)
-    constexpr const number_float_t* get_impl_ptr(const number_float_t*) const noexcept
+    constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept
     {
         return is_number_float() ? &m_value.number_float : nullptr;
     }
@@ -2845,34 +3150,69 @@
         {
             return *ptr;
         }
-        else
-        {
-            throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " +
-                                    obj.type_name());
-        }
+
+        throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " +
+                                obj.type_name());
     }
 
   public:
-
     /// @name value access
     /// Direct access to the stored value of a JSON value.
     /// @{
 
     /*!
+    @brief get special-case overload
+
+    This overloads avoids a lot of template boilerplate, it can be seen as the
+    identity method
+
+    @tparam BasicJsonType == @ref basic_json
+
+    @return a copy of *this
+
+    @complexity Constant.
+
+    @since version 2.1.0
+    */
+    template <
+        typename BasicJsonType,
+        detail::enable_if_t<std::is_same<typename std::remove_const<BasicJsonType>::type,
+                                         basic_json_t>::value,
+                            int> = 0 >
+    basic_json get() const
+    {
+        return *this;
+    }
+
+    /*!
     @brief get a value (explicit)
 
-    Explicit type conversion between the JSON value and a compatible value.
+    Explicit type conversion between the JSON value and a compatible value
+    which is [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible)
+    and [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible).
+    The value is converted by calling the @ref json_serializer<ValueType>
+    `from_json()` method.
 
-    @tparam ValueType non-pointer type compatible to the JSON value, for
-    instance `int` for JSON integer numbers, `bool` for JSON booleans, or
-    `std::vector` types for JSON arrays
+    The function is equivalent to executing
+    @code {.cpp}
+    ValueType ret;
+    JSONSerializer<ValueType>::from_json(*this, ret);
+    return ret;
+    @endcode
 
-    @return copy of the JSON value, converted to type @a ValueType
+    This overloads is chosen if:
+    - @a ValueType is not @ref basic_json,
+    - @ref json_serializer<ValueType> has a `from_json()` method of the form
+      `void from_json(const @ref basic_json&, ValueType&)`, and
+    - @ref json_serializer<ValueType> does not have a `from_json()` method of
+      the form `ValueType from_json(const @ref basic_json&)`
 
-    @throw std::domain_error in case passed type @a ValueType is incompatible
-    to JSON; example: `"type must be object, but is null"`
+    @tparam ValueTypeCV the provided value type
+    @tparam ValueType the returned value type
 
-    @complexity Linear in the size of the JSON value.
+    @return copy of the JSON value, converted to @a ValueType
+
+    @throw what @ref json_serializer<ValueType> `from_json()` method throws
 
     @liveexample{The example below shows several conversions from JSON values
     to other types. There a few things to note: (1) Floating-point numbers can
@@ -2881,21 +3221,75 @@
     associative containers such as `std::unordered_map<std::string\,
     json>`.,get__ValueType_const}
 
-    @internal
-    The idea of using a casted null pointer to choose the correct
-    implementation is from <http://stackoverflow.com/a/8315197/266378>.
-    @endinternal
-
-    @sa @ref operator ValueType() const for implicit conversion
-    @sa @ref get() for pointer-member access
-
-    @since version 1.0.0
+    @since version 2.1.0
     */
-    template<typename ValueType, typename std::enable_if<
-                 not std::is_pointer<ValueType>::value, int>::type = 0>
-    ValueType get() const
+    template <
+        typename ValueTypeCV,
+        typename ValueType = detail::uncvref_t<ValueTypeCV>,
+        detail::enable_if_t <
+            not std::is_same<basic_json_t, ValueType>::value and
+            detail::has_from_json<basic_json_t, ValueType>::value and
+            not detail::has_non_default_from_json<basic_json_t, ValueType>::value,
+            int > = 0 >
+    ValueType get() const noexcept(noexcept(
+                                       JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>())))
     {
-        return get_impl(static_cast<ValueType*>(nullptr));
+        // we cannot static_assert on ValueTypeCV being non-const, because
+        // there is support for get<const basic_json_t>(), which is why we
+        // still need the uncvref
+        static_assert(not std::is_reference<ValueTypeCV>::value,
+                      "get() cannot be used with reference types, you might want to use get_ref()");
+        static_assert(std::is_default_constructible<ValueType>::value,
+                      "types must be DefaultConstructible when used with get()");
+
+        ValueType ret;
+        JSONSerializer<ValueType>::from_json(*this, ret);
+        return ret;
+    }
+
+    /*!
+    @brief get a value (explicit); special case
+
+    Explicit type conversion between the JSON value and a compatible value
+    which is **not** [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible)
+    and **not** [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible).
+    The value is converted by calling the @ref json_serializer<ValueType>
+    `from_json()` method.
+
+    The function is equivalent to executing
+    @code {.cpp}
+    return JSONSerializer<ValueTypeCV>::from_json(*this);
+    @endcode
+
+    This overloads is chosen if:
+    - @a ValueType is not @ref basic_json and
+    - @ref json_serializer<ValueType> has a `from_json()` method of the form
+      `ValueType from_json(const @ref basic_json&)`
+
+    @note If @ref json_serializer<ValueType> has both overloads of
+    `from_json()`, this one is chosen.
+
+    @tparam ValueTypeCV the provided value type
+    @tparam ValueType the returned value type
+
+    @return copy of the JSON value, converted to @a ValueType
+
+    @throw what @ref json_serializer<ValueType> `from_json()` method throws
+
+    @since version 2.1.0
+    */
+    template <
+        typename ValueTypeCV,
+        typename ValueType = detail::uncvref_t<ValueTypeCV>,
+        detail::enable_if_t<not std::is_same<basic_json_t, ValueType>::value and
+                            detail::has_non_default_from_json<basic_json_t,
+                                    ValueType>::value, int> = 0 >
+    ValueType get() const noexcept(noexcept(
+                                       JSONSerializer<ValueTypeCV>::from_json(std::declval<const basic_json_t&>())))
+    {
+        static_assert(not std::is_reference<ValueTypeCV>::value,
+                      "get() cannot be used with reference types, you might want to use get_ref()");
+        return JSONSerializer<ValueTypeCV>::from_json(*this);
     }
 
     /*!
@@ -3100,7 +3494,7 @@
     template < typename ValueType, typename std::enable_if <
                    not std::is_pointer<ValueType>::value and
                    not std::is_same<ValueType, typename string_t::value_type>::value
-#ifndef _MSC_VER  // Fix for issue #167 operator<< abiguity under VS2015
+#ifndef _MSC_VER  // fix for issue #167 operator<< abiguity under VS2015
                    and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value
 #endif
                    , int >::type = 0 >
@@ -3148,19 +3542,19 @@
         // at only works for arrays
         if (is_array())
         {
-            try
+            JSON_TRY
             {
                 return m_value.array->at(idx);
             }
-            catch (std::out_of_range&)
+            JSON_CATCH (std::out_of_range&)
             {
                 // create better exception explanation
-                throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+                JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range"));
             }
         }
         else
         {
-            throw std::domain_error("cannot use at() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use at() with " + type_name()));
         }
     }
 
@@ -3191,19 +3585,19 @@
         // at only works for arrays
         if (is_array())
         {
-            try
+            JSON_TRY
             {
                 return m_value.array->at(idx);
             }
-            catch (std::out_of_range&)
+            JSON_CATCH (std::out_of_range&)
             {
                 // create better exception explanation
-                throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+                JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range"));
             }
         }
         else
         {
-            throw std::domain_error("cannot use at() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use at() with " + type_name()));
         }
     }
 
@@ -3238,19 +3632,19 @@
         // at only works for objects
         if (is_object())
         {
-            try
+            JSON_TRY
             {
                 return m_value.object->at(key);
             }
-            catch (std::out_of_range&)
+            JSON_CATCH (std::out_of_range&)
             {
                 // create better exception explanation
-                throw std::out_of_range("key '" + key + "' not found");
+                JSON_THROW(std::out_of_range("key '" + key + "' not found"));
             }
         }
         else
         {
-            throw std::domain_error("cannot use at() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use at() with " + type_name()));
         }
     }
 
@@ -3285,19 +3679,19 @@
         // at only works for objects
         if (is_object())
         {
-            try
+            JSON_TRY
             {
                 return m_value.object->at(key);
             }
-            catch (std::out_of_range&)
+            JSON_CATCH (std::out_of_range&)
             {
                 // create better exception explanation
-                throw std::out_of_range("key '" + key + "' not found");
+                JSON_THROW(std::out_of_range("key '" + key + "' not found"));
             }
         }
         else
         {
-            throw std::domain_error("cannot use at() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use at() with " + type_name()));
         }
     }
 
@@ -3349,10 +3743,8 @@
 
             return m_value.array->operator[](idx);
         }
-        else
-        {
-            throw std::domain_error("cannot use operator[] with " + type_name());
-        }
+
+        JSON_THROW(std::domain_error("cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -3381,10 +3773,8 @@
         {
             return m_value.array->operator[](idx);
         }
-        else
-        {
-            throw std::domain_error("cannot use operator[] with " + type_name());
-        }
+
+        JSON_THROW(std::domain_error("cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -3429,10 +3819,8 @@
         {
             return m_value.object->operator[](key);
         }
-        else
-        {
-            throw std::domain_error("cannot use operator[] with " + type_name());
-        }
+
+        JSON_THROW(std::domain_error("cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -3473,10 +3861,8 @@
             assert(m_value.object->find(key) != m_value.object->end());
             return m_value.object->find(key)->second;
         }
-        else
-        {
-            throw std::domain_error("cannot use operator[] with " + type_name());
-        }
+
+        JSON_THROW(std::domain_error("cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -3590,10 +3976,8 @@
         {
             return m_value.object->operator[](key);
         }
-        else
-        {
-            throw std::domain_error("cannot use operator[] with " + type_name());
-        }
+
+        JSON_THROW(std::domain_error("cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -3635,10 +4019,8 @@
             assert(m_value.object->find(key) != m_value.object->end());
             return m_value.object->find(key)->second;
         }
-        else
-        {
-            throw std::domain_error("cannot use operator[] with " + type_name());
-        }
+
+        JSON_THROW(std::domain_error("cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -3702,14 +4084,12 @@
             {
                 return *it;
             }
-            else
-            {
-                return default_value;
-            }
+
+            return default_value;
         }
         else
         {
-            throw std::domain_error("cannot use value() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use value() with " + type_name()));
         }
     }
 
@@ -3771,19 +4151,17 @@
         if (is_object())
         {
             // if pointer resolves a value, return it or use default value
-            try
+            JSON_TRY
             {
                 return ptr.get_checked(this);
             }
-            catch (std::out_of_range&)
+            JSON_CATCH (std::out_of_range&)
             {
                 return default_value;
             }
         }
-        else
-        {
-            throw std::domain_error("cannot use value() with " + type_name());
-        }
+
+        JSON_THROW(std::domain_error("cannot use value() with " + type_name()));
     }
 
     /*!
@@ -3934,7 +4312,7 @@
         // make sure iterator fits the current value
         if (this != pos.m_object)
         {
-            throw std::domain_error("iterator does not fit current value");
+            JSON_THROW(std::domain_error("iterator does not fit current value"));
         }
 
         IteratorType result = end();
@@ -3949,7 +4327,7 @@
             {
                 if (not pos.m_it.primitive_iterator.is_begin())
                 {
-                    throw std::out_of_range("iterator out of range");
+                    JSON_THROW(std::out_of_range("iterator out of range"));
                 }
 
                 if (is_string())
@@ -3979,7 +4357,7 @@
 
             default:
             {
-                throw std::domain_error("cannot use erase() with " + type_name());
+                JSON_THROW(std::domain_error("cannot use erase() with " + type_name()));
             }
         }
 
@@ -4041,7 +4419,7 @@
         // make sure iterator fits the current value
         if (this != first.m_object or this != last.m_object)
         {
-            throw std::domain_error("iterators do not fit current value");
+            JSON_THROW(std::domain_error("iterators do not fit current value"));
         }
 
         IteratorType result = end();
@@ -4056,7 +4434,7 @@
             {
                 if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end())
                 {
-                    throw std::out_of_range("iterators out of range");
+                    JSON_THROW(std::out_of_range("iterators out of range"));
                 }
 
                 if (is_string())
@@ -4088,7 +4466,7 @@
 
             default:
             {
-                throw std::domain_error("cannot use erase() with " + type_name());
+                JSON_THROW(std::domain_error("cannot use erase() with " + type_name()));
             }
         }
 
@@ -4131,10 +4509,8 @@
         {
             return m_value.object->erase(key);
         }
-        else
-        {
-            throw std::domain_error("cannot use erase() with " + type_name());
-        }
+
+        JSON_THROW(std::domain_error("cannot use erase() with " + type_name()));
     }
 
     /*!
@@ -4168,14 +4544,14 @@
         {
             if (idx >= size())
             {
-                throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+                JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range"));
             }
 
             m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));
         }
         else
         {
-            throw std::domain_error("cannot use erase() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use erase() with " + type_name()));
         }
     }
 
@@ -4893,7 +5269,7 @@
         // push_back only works for null objects or arrays
         if (not(is_null() or is_array()))
         {
-            throw std::domain_error("cannot use push_back() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use push_back() with " + type_name()));
         }
 
         // transform null object into an array
@@ -4929,7 +5305,7 @@
         // push_back only works for null objects or arrays
         if (not(is_null() or is_array()))
         {
-            throw std::domain_error("cannot use push_back() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use push_back() with " + type_name()));
         }
 
         // transform null object into an array
@@ -4979,7 +5355,7 @@
         // push_back only works for null objects or objects
         if (not(is_null() or is_object()))
         {
-            throw std::domain_error("cannot use push_back() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use push_back() with " + type_name()));
         }
 
         // transform null object into an object
@@ -5079,7 +5455,7 @@
         // emplace_back only works for null objects or arrays
         if (not(is_null() or is_array()))
         {
-            throw std::domain_error("cannot use emplace_back() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use emplace_back() with " + type_name()));
         }
 
         // transform null object into an array
@@ -5097,8 +5473,8 @@
     /*!
     @brief add an object to an object if key does not exist
 
-    Inserts a new element into a JSON object constructed in-place with the given
-    @a args if there is no element with the key in the container. If the
+    Inserts a new element into a JSON object constructed in-place with the
+    given @a args if there is no element with the key in the container. If the
     function is called on a JSON null value, an empty object is created before
     appending the value created from @a args.
 
@@ -5127,7 +5503,7 @@
         // emplace only works for null objects or arrays
         if (not(is_null() or is_object()))
         {
-            throw std::domain_error("cannot use emplace() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use emplace() with " + type_name()));
         }
 
         // transform null object into an object
@@ -5163,8 +5539,8 @@
     @throw std::domain_error if @a pos is not an iterator of *this; example:
     `"iterator does not fit current value"`
 
-    @complexity Constant plus linear in the distance between pos and end of the
-    container.
+    @complexity Constant plus linear in the distance between pos and end of
+    the container.
 
     @liveexample{The example shows how `insert()` is used.,insert}
 
@@ -5178,7 +5554,7 @@
             // check if iterator pos fits to this JSON value
             if (pos.m_object != this)
             {
-                throw std::domain_error("iterator does not fit current value");
+                JSON_THROW(std::domain_error("iterator does not fit current value"));
             }
 
             // insert to array and return iterator
@@ -5186,10 +5562,8 @@
             result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val);
             return result;
         }
-        else
-        {
-            throw std::domain_error("cannot use insert() with " + type_name());
-        }
+
+        JSON_THROW(std::domain_error("cannot use insert() with " + type_name()));
     }
 
     /*!
@@ -5233,7 +5607,7 @@
             // check if iterator pos fits to this JSON value
             if (pos.m_object != this)
             {
-                throw std::domain_error("iterator does not fit current value");
+                JSON_THROW(std::domain_error("iterator does not fit current value"));
             }
 
             // insert to array and return iterator
@@ -5241,10 +5615,8 @@
             result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);
             return result;
         }
-        else
-        {
-            throw std::domain_error("cannot use insert() with " + type_name());
-        }
+
+        JSON_THROW(std::domain_error("cannot use insert() with " + type_name()));
     }
 
     /*!
@@ -5282,24 +5654,24 @@
         // insert only works for arrays
         if (not is_array())
         {
-            throw std::domain_error("cannot use insert() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use insert() with " + type_name()));
         }
 
         // check if iterator pos fits to this JSON value
         if (pos.m_object != this)
         {
-            throw std::domain_error("iterator does not fit current value");
+            JSON_THROW(std::domain_error("iterator does not fit current value"));
         }
 
         // check if range iterators belong to the same JSON object
         if (first.m_object != last.m_object)
         {
-            throw std::domain_error("iterators do not fit");
+            JSON_THROW(std::domain_error("iterators do not fit"));
         }
 
         if (first.m_object == this or last.m_object == this)
         {
-            throw std::domain_error("passed iterators may not belong to container");
+            JSON_THROW(std::domain_error("passed iterators may not belong to container"));
         }
 
         // insert to array and return iterator
@@ -5340,13 +5712,13 @@
         // insert only works for arrays
         if (not is_array())
         {
-            throw std::domain_error("cannot use insert() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use insert() with " + type_name()));
         }
 
         // check if iterator pos fits to this JSON value
         if (pos.m_object != this)
         {
-            throw std::domain_error("iterator does not fit current value");
+            JSON_THROW(std::domain_error("iterator does not fit current value"));
         }
 
         // insert to array and return iterator
@@ -5394,8 +5766,8 @@
 
     @param[in,out] other array to exchange the contents with
 
-    @throw std::domain_error when JSON value is not an array; example: `"cannot
-    use swap() with string"`
+    @throw std::domain_error when JSON value is not an array; example:
+    `"cannot use swap() with string"`
 
     @complexity Constant.
 
@@ -5413,7 +5785,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use swap() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use swap() with " + type_name()));
         }
     }
 
@@ -5446,7 +5818,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use swap() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use swap() with " + type_name()));
         }
     }
 
@@ -5479,13 +5851,13 @@
         }
         else
         {
-            throw std::domain_error("cannot use swap() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use swap() with " + type_name()));
         }
     }
 
     /// @}
 
-
+  public:
     //////////////////////////////////////////
     // lexicographical comparison operators //
     //////////////////////////////////////////
@@ -5493,40 +5865,6 @@
     /// @name lexicographical comparison operators
     /// @{
 
-  private:
-    /*!
-    @brief comparison operator for JSON types
-
-    Returns an ordering that is similar to Python:
-    - order: null < boolean < number < object < array < string
-    - furthermore, each type is not smaller than itself
-
-    @since version 1.0.0
-    */
-    friend bool operator<(const value_t lhs, const value_t rhs) noexcept
-    {
-        static constexpr std::array<uint8_t, 8> order = {{
-                0, // null
-                3, // object
-                4, // array
-                5, // string
-                1, // boolean
-                2, // integer
-                2, // unsigned
-                2, // float
-            }
-        };
-
-        // discarded values are not comparable
-        if (lhs == value_t::discarded or rhs == value_t::discarded)
-        {
-            return false;
-        }
-
-        return order[static_cast<std::size_t>(lhs)] < order[static_cast<std::size_t>(rhs)];
-    }
-
-  public:
     /*!
     @brief comparison: equal
 
@@ -6122,7 +6460,7 @@
     {
         // assertion to check that the iterator range is indeed contiguous,
         // see http://stackoverflow.com/a/35008842/266378 for more discussion
-        assert(std::accumulate(first, last, std::make_pair<bool, int>(true, 0),
+        assert(std::accumulate(first, last, std::pair<bool, int>(true, 0),
                                [&first](std::pair<bool, int> res, decltype(*first) val)
         {
             res.first &= (val == *(std::next(std::addressof(*first), res.second++)));
@@ -6323,11 +6661,11 @@
     {
         if (current_index + sizeof(T) + 1 > vec.size())
         {
-            throw std::out_of_range("cannot read " + std::to_string(sizeof(T)) + " bytes from vector");
+            JSON_THROW(std::out_of_range("cannot read " + std::to_string(sizeof(T)) + " bytes from vector"));
         }
 
         T result;
-        uint8_t* ptr = reinterpret_cast<uint8_t*>(&result);
+        auto* ptr = reinterpret_cast<uint8_t*>(&result);
         for (size_t i = 0; i < sizeof(T); ++i)
         {
             *ptr++ = vec[current_index + sizeof(T) - i];
@@ -6368,8 +6706,9 @@
                 if (j.m_value.number_integer >= 0)
                 {
                     // MessagePack does not differentiate between positive
-                    // signed integers and unsigned integers. Therefore, we used
-                    // the code from the value_t::number_unsigned case here.
+                    // signed integers and unsigned integers. Therefore, we
+                    // used the code from the value_t::number_unsigned case
+                    // here.
                     if (j.m_value.number_unsigned < 128)
                     {
                         // positive fixnum
@@ -6473,7 +6812,7 @@
             {
                 // float 64
                 v.push_back(0xcb);
-                const uint8_t* helper = reinterpret_cast<const uint8_t*>(&(j.m_value.number_float));
+                const auto* helper = reinterpret_cast<const uint8_t*>(&(j.m_value.number_float));
                 for (size_t i = 0; i < 8; ++i)
                 {
                     v.push_back(helper[7 - i]);
@@ -6644,8 +6983,8 @@
                 }
                 else
                 {
-                    // The conversions below encode the sign in the first byte,
-                    // and the value is converted to a positive number.
+                    // The conversions below encode the sign in the first
+                    // byte, and the value is converted to a positive number.
                     const auto positive_number = -1 - j.m_value.number_integer;
                     if (j.m_value.number_integer >= -24)
                     {
@@ -6716,7 +7055,7 @@
             {
                 // Double-Precision Float
                 v.push_back(0xfb);
-                const uint8_t* helper = reinterpret_cast<const uint8_t*>(&(j.m_value.number_float));
+                const auto* helper = reinterpret_cast<const uint8_t*>(&(j.m_value.number_float));
                 for (size_t i = 0; i < 8; ++i)
                 {
                     v.push_back(helper[7 - i]);
@@ -6850,12 +7189,12 @@
 
     To secure the access to the byte vector during CBOR/MessagePack
     deserialization, bytes are copied from the vector into buffers. This
-    function checks if the number of bytes to copy (@a len) does not exceed the
-    size @s size of the vector. Additionally, an @a offset is given from where
-    to start reading the bytes.
+    function checks if the number of bytes to copy (@a len) does not exceed
+    the size @s size of the vector. Additionally, an @a offset is given from
+    where to start reading the bytes.
 
-    This function checks whether reading the bytes is safe; that is, offset is a
-    valid index in the vector, offset+len
+    This function checks whether reading the bytes is safe; that is, offset is
+    a valid index in the vector, offset+len
 
     @param[in] size    size of the byte vector
     @param[in] len     number of bytes to read
@@ -6872,19 +7211,19 @@
         // simple case: requested length is greater than the vector's length
         if (len > size or offset > size)
         {
-            throw std::out_of_range("len out of range");
+            JSON_THROW(std::out_of_range("len out of range"));
         }
 
         // second case: adding offset would result in overflow
         if ((size > (std::numeric_limits<size_t>::max() - offset)))
         {
-            throw std::out_of_range("len+offset out of range");
+            JSON_THROW(std::out_of_range("len+offset out of range"));
         }
 
         // last case: reading past the end of the vector
         if (len + offset > size)
         {
-            throw std::out_of_range("len+offset out of range");
+            JSON_THROW(std::out_of_range("len+offset out of range"));
         }
     }
 
@@ -6916,7 +7255,7 @@
             {
                 return v[current_idx];
             }
-            else if (v[current_idx] <= 0x8f) // fixmap
+            if (v[current_idx] <= 0x8f) // fixmap
             {
                 basic_json result = value_t::object;
                 const size_t len = v[current_idx] & 0x0f;
@@ -6972,11 +7311,10 @@
                 case 0xca: // float 32
                 {
                     // copy bytes in reverse order into the double variable
-                    check_length(v.size(), sizeof(float), 1);
                     float res;
                     for (size_t byte = 0; byte < sizeof(float); ++byte)
                     {
-                        reinterpret_cast<uint8_t*>(&res)[sizeof(float) - byte - 1] = v[current_idx + 1 + byte];
+                        reinterpret_cast<uint8_t*>(&res)[sizeof(float) - byte - 1] = v.at(current_idx + 1 + byte);
                     }
                     idx += sizeof(float); // skip content bytes
                     return res;
@@ -6985,11 +7323,10 @@
                 case 0xcb: // float 64
                 {
                     // copy bytes in reverse order into the double variable
-                    check_length(v.size(), sizeof(double), 1);
                     double res;
                     for (size_t byte = 0; byte < sizeof(double); ++byte)
                     {
-                        reinterpret_cast<uint8_t*>(&res)[sizeof(double) - byte - 1] = v[current_idx + 1 + byte];
+                        reinterpret_cast<uint8_t*>(&res)[sizeof(double) - byte - 1] = v.at(current_idx + 1 + byte);
                     }
                     idx += sizeof(double); // skip content bytes
                     return res;
@@ -7122,7 +7459,7 @@
 
                 default:
                 {
-                    throw std::invalid_argument("error parsing a msgpack @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast<int>(v[current_idx])));
+                    JSON_THROW(std::invalid_argument("error parsing a msgpack @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast<int>(v[current_idx]))));
                 }
             }
         }
@@ -7551,7 +7888,6 @@
 
             case 0xf9: // Half-Precision Float (two-byte IEEE 754)
             {
-                check_length(v.size(), 2, 1);
                 idx += 2; // skip two content bytes
 
                 // code from RFC 7049, Appendix D, Figure 3:
@@ -7561,7 +7897,7 @@
                 // include at least decoding support for them even without such
                 // support. An example of a small decoder for half-precision
                 // floating-point numbers in the C language is shown in Fig. 3.
-                const int half = (v[current_idx + 1] << 8) + v[current_idx + 2];
+                const int half = (v.at(current_idx + 1) << 8) + v.at(current_idx + 2);
                 const int exp = (half >> 10) & 0x1f;
                 const int mant = half & 0x3ff;
                 double val;
@@ -7577,17 +7913,16 @@
                 {
                     val = mant == 0 ? INFINITY : NAN;
                 }
-                return half & 0x8000 ? -val : val;
+                return (half & 0x8000) != 0 ? -val : val;
             }
 
             case 0xfa: // Single-Precision Float (four-byte IEEE 754)
             {
                 // copy bytes in reverse order into the float variable
-                check_length(v.size(), sizeof(float), 1);
                 float res;
                 for (size_t byte = 0; byte < sizeof(float); ++byte)
                 {
-                    reinterpret_cast<uint8_t*>(&res)[sizeof(float) - byte - 1] = v[current_idx + 1 + byte];
+                    reinterpret_cast<uint8_t*>(&res)[sizeof(float) - byte - 1] = v.at(current_idx + 1 + byte);
                 }
                 idx += sizeof(float); // skip content bytes
                 return res;
@@ -7595,12 +7930,11 @@
 
             case 0xfb: // Double-Precision Float (eight-byte IEEE 754)
             {
-                check_length(v.size(), sizeof(double), 1);
                 // copy bytes in reverse order into the double variable
                 double res;
                 for (size_t byte = 0; byte < sizeof(double); ++byte)
                 {
-                    reinterpret_cast<uint8_t*>(&res)[sizeof(double) - byte - 1] = v[current_idx + 1 + byte];
+                    reinterpret_cast<uint8_t*>(&res)[sizeof(double) - byte - 1] = v.at(current_idx + 1 + byte);
                 }
                 idx += sizeof(double); // skip content bytes
                 return res;
@@ -7608,7 +7942,7 @@
 
             default: // anything else (0xFF is handled inside the other types)
             {
-                throw std::invalid_argument("error parsing a CBOR @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast<int>(v[current_idx])));
+                JSON_THROW(std::invalid_argument("error parsing a CBOR @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast<int>(v[current_idx]))));
             }
         }
     }
@@ -7728,7 +8062,6 @@
 
     /// @}
 
-  private:
     ///////////////////////////
     // convenience functions //
     ///////////////////////////
@@ -7743,29 +8076,35 @@
 
     @complexity Constant.
 
-    @since version 1.0.0
+    @liveexample{The following code exemplifies `type_name()` for all JSON
+    types.,type_name}
+
+    @since version 1.0.0, public since 2.1.0
     */
     std::string type_name() const
     {
-        switch (m_type)
         {
-            case value_t::null:
-                return "null";
-            case value_t::object:
-                return "object";
-            case value_t::array:
-                return "array";
-            case value_t::string:
-                return "string";
-            case value_t::boolean:
-                return "boolean";
-            case value_t::discarded:
-                return "discarded";
-            default:
-                return "number";
+            switch (m_type)
+            {
+                case value_t::null:
+                    return "null";
+                case value_t::object:
+                    return "object";
+                case value_t::array:
+                    return "array";
+                case value_t::string:
+                    return "string";
+                case value_t::boolean:
+                    return "boolean";
+                case value_t::discarded:
+                    return "discarded";
+                default:
+                    return "number";
+            }
         }
     }
 
+  private:
     /*!
     @brief calculates the extra space to escape a JSON string
 
@@ -7800,10 +8139,8 @@
                         // from c (1 byte) to \uxxxx (6 bytes)
                         return res + 5;
                     }
-                    else
-                    {
-                        return res;
-                    }
+
+                    return res;
                 }
             }
         });
@@ -8115,6 +8452,11 @@
     class primitive_iterator_t
     {
       public:
+
+        difference_type get_value() const noexcept
+        {
+            return m_it;
+        }
         /// set iterator to a defined beginning
         void set_begin() noexcept
         {
@@ -8139,16 +8481,87 @@
             return (m_it == end_value);
         }
 
-        /// return reference to the value to change and compare
-        operator difference_type& () noexcept
+        friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
         {
-            return m_it;
+            return lhs.m_it == rhs.m_it;
         }
 
-        /// return value to compare
-        constexpr operator difference_type () const noexcept
+        friend constexpr bool operator!=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
         {
-            return m_it;
+            return !(lhs == rhs);
+        }
+
+        friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+        {
+            return lhs.m_it < rhs.m_it;
+        }
+
+        friend constexpr bool operator<=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+        {
+            return lhs.m_it <= rhs.m_it;
+        }
+
+        friend constexpr bool operator>(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+        {
+            return lhs.m_it > rhs.m_it;
+        }
+
+        friend constexpr bool operator>=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+        {
+            return lhs.m_it >= rhs.m_it;
+        }
+
+        primitive_iterator_t operator+(difference_type i)
+        {
+            auto result = *this;
+            result += i;
+            return result;
+        }
+
+        friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+        {
+            return lhs.m_it - rhs.m_it;
+        }
+
+        friend std::ostream& operator<<(std::ostream& os, primitive_iterator_t it)
+        {
+            return os << it.m_it;
+        }
+
+        primitive_iterator_t& operator++()
+        {
+            ++m_it;
+            return *this;
+        }
+
+        primitive_iterator_t& operator++(int)
+        {
+            m_it++;
+            return *this;
+        }
+
+        primitive_iterator_t& operator--()
+        {
+            --m_it;
+            return *this;
+        }
+
+        primitive_iterator_t& operator--(int)
+        {
+            m_it--;
+            return *this;
+        }
+
+        primitive_iterator_t& operator+=(difference_type n)
+        {
+            m_it += n;
+            return *this;
+        }
+
+        primitive_iterator_t& operator-=(difference_type n)
+        {
+            m_it -= n;
+            return *this;
         }
 
       private:
@@ -8500,7 +8913,7 @@
 
                 case basic_json::value_t::null:
                 {
-                    throw std::out_of_range("cannot get value");
+                    JSON_THROW(std::out_of_range("cannot get value"));
                 }
 
                 default:
@@ -8509,10 +8922,8 @@
                     {
                         return *m_object;
                     }
-                    else
-                    {
-                        throw std::out_of_range("cannot get value");
-                    }
+
+                    JSON_THROW(std::out_of_range("cannot get value"));
                 }
             }
         }
@@ -8545,10 +8956,8 @@
                     {
                         return m_object;
                     }
-                    else
-                    {
-                        throw std::out_of_range("cannot get value");
-                    }
+
+                    JSON_THROW(std::out_of_range("cannot get value"));
                 }
             }
         }
@@ -8648,7 +9057,7 @@
             // if objects are not the same, the comparison is undefined
             if (m_object != other.m_object)
             {
-                throw std::domain_error("cannot compare iterators of different containers");
+                JSON_THROW(std::domain_error("cannot compare iterators of different containers"));
             }
 
             assert(m_object != nullptr);
@@ -8690,7 +9099,7 @@
             // if objects are not the same, the comparison is undefined
             if (m_object != other.m_object)
             {
-                throw std::domain_error("cannot compare iterators of different containers");
+                JSON_THROW(std::domain_error("cannot compare iterators of different containers"));
             }
 
             assert(m_object != nullptr);
@@ -8699,7 +9108,7 @@
             {
                 case basic_json::value_t::object:
                 {
-                    throw std::domain_error("cannot compare order of object iterators");
+                    JSON_THROW(std::domain_error("cannot compare order of object iterators"));
                 }
 
                 case basic_json::value_t::array:
@@ -8753,7 +9162,7 @@
             {
                 case basic_json::value_t::object:
                 {
-                    throw std::domain_error("cannot use offsets with object iterators");
+                    JSON_THROW(std::domain_error("cannot use offsets with object iterators"));
                 }
 
                 case basic_json::value_t::array:
@@ -8815,7 +9224,7 @@
             {
                 case basic_json::value_t::object:
                 {
-                    throw std::domain_error("cannot use offsets with object iterators");
+                    JSON_THROW(std::domain_error("cannot use offsets with object iterators"));
                 }
 
                 case basic_json::value_t::array:
@@ -8842,7 +9251,7 @@
             {
                 case basic_json::value_t::object:
                 {
-                    throw std::domain_error("cannot use operator[] for object iterators");
+                    JSON_THROW(std::domain_error("cannot use operator[] for object iterators"));
                 }
 
                 case basic_json::value_t::array:
@@ -8852,19 +9261,17 @@
 
                 case basic_json::value_t::null:
                 {
-                    throw std::out_of_range("cannot get value");
+                    JSON_THROW(std::out_of_range("cannot get value"));
                 }
 
                 default:
                 {
-                    if (m_it.primitive_iterator == -n)
+                    if (m_it.primitive_iterator.get_value() == -n)
                     {
                         return *m_object;
                     }
-                    else
-                    {
-                        throw std::out_of_range("cannot get value");
-                    }
+
+                    JSON_THROW(std::out_of_range("cannot get value"));
                 }
             }
         }
@@ -8881,10 +9288,8 @@
             {
                 return m_it.object_iterator->first;
             }
-            else
-            {
-                throw std::domain_error("cannot use key() for non-object iterators");
-            }
+
+            JSON_THROW(std::domain_error("cannot use key() for non-object iterators"));
         }
 
         /*!
@@ -9069,7 +9474,7 @@
             // immediately abort if stream is erroneous
             if (s.fail())
             {
-                throw std::invalid_argument("stream error");
+                JSON_THROW(std::invalid_argument("stream error"));
             }
 
             // fill buffer
@@ -9136,7 +9541,7 @@
                 }
                 else
                 {
-                    throw std::invalid_argument("missing or wrong low surrogate");
+                    JSON_THROW(std::invalid_argument("missing or wrong low surrogate"));
                 }
             }
 
@@ -9170,7 +9575,7 @@
             }
             else
             {
-                throw std::out_of_range("code points above 0x10FFFF are invalid");
+                JSON_THROW(std::out_of_range("code points above 0x10FFFF are invalid"));
             }
 
             return result;
@@ -10209,7 +10614,7 @@
             assert(m_marker == nullptr or m_marker  <= m_limit);
 
             // number of processed characters (p)
-            const size_t num_processed_chars = static_cast<size_t>(m_start - m_content);
+            const auto num_processed_chars = static_cast<size_t>(m_start - m_content);
             // offset for m_marker wrt. to m_start
             const auto offset_marker = (m_marker == nullptr) ? 0 : m_marker - m_start;
             // number of unprocessed characters (u)
@@ -10402,7 +10807,7 @@
                                 // make sure there is a subsequent unicode
                                 if ((i + 6 >= m_limit) or * (i + 5) != '\\' or * (i + 6) != 'u')
                                 {
-                                    throw std::invalid_argument("missing low surrogate");
+                                    JSON_THROW(std::invalid_argument("missing low surrogate"));
                                 }
 
                                 // get code yyyy from uxxxx\uyyyy
@@ -10415,7 +10820,7 @@
                             else if (codepoint >= 0xDC00 and codepoint <= 0xDFFF)
                             {
                                 // we found a lone low surrogate
-                                throw std::invalid_argument("missing high surrogate");
+                                JSON_THROW(std::invalid_argument("missing high surrogate"));
                             }
                             else
                             {
@@ -10540,7 +10945,7 @@
             for (; curptr < m_cursor; curptr++)
             {
                 // quickly skip tests if a digit
-                if (*curptr < '0' || *curptr > '9')
+                if (*curptr < '0' or* curptr > '9')
                 {
                     if (*curptr == '.')
                     {
@@ -10601,7 +11006,7 @@
             else
             {
                 // parse with strtod
-                result.m_value.number_float = str_to_float_t(static_cast<number_float_t*>(nullptr), NULL);
+                result.m_value.number_float = str_to_float_t(static_cast<number_float_t*>(nullptr), nullptr);
 
                 // replace infinity and NAN by null
                 if (not std::isfinite(result.m_value.number_float))
@@ -10893,7 +11298,7 @@
                               "'") :
                               lexer::token_type_name(last_token));
                 error_msg += "; expected " + lexer::token_type_name(t);
-                throw std::invalid_argument(error_msg);
+                JSON_THROW(std::invalid_argument(error_msg));
             }
         }
 
@@ -10905,7 +11310,7 @@
                 error_msg += (last_token == lexer::token_type::parse_error ? ("'" +  m_lexer.get_token_string() +
                               "'") :
                               lexer::token_type_name(last_token));
-                throw std::invalid_argument(error_msg);
+                JSON_THROW(std::invalid_argument(error_msg));
             }
         }
 
@@ -11001,7 +11406,7 @@
         {
             if (is_root())
             {
-                throw std::domain_error("JSON pointer has no parent");
+                JSON_THROW(std::domain_error("JSON pointer has no parent"));
             }
 
             auto last = reference_tokens.back();
@@ -11019,7 +11424,7 @@
         {
             if (is_root())
             {
-                throw std::domain_error("JSON pointer has no parent");
+                JSON_THROW(std::domain_error("JSON pointer has no parent"));
             }
 
             json_pointer result = *this;
@@ -11080,7 +11485,7 @@
                     */
                     default:
                     {
-                        throw std::domain_error("invalid value to unflatten");
+                        JSON_THROW(std::domain_error("invalid value to unflatten"));
                     }
                 }
             }
@@ -11148,7 +11553,7 @@
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            throw std::domain_error("array index must not begin with '0'");
+                            JSON_THROW(std::domain_error("array index must not begin with '0'"));
                         }
 
                         if (reference_token == "-")
@@ -11166,7 +11571,7 @@
 
                     default:
                     {
-                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                        JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'"));
                     }
                 }
             }
@@ -11200,7 +11605,7 @@
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            throw std::domain_error("array index must not begin with '0'");
+                            JSON_THROW(std::domain_error("array index must not begin with '0'"));
                         }
 
                         // note: at performs range check
@@ -11210,7 +11615,7 @@
 
                     default:
                     {
-                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                        JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'"));
                     }
                 }
             }
@@ -11252,7 +11657,7 @@
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            throw std::domain_error("array index must not begin with '0'");
+                            JSON_THROW(std::domain_error("array index must not begin with '0'"));
                         }
 
                         // use unchecked array access
@@ -11262,7 +11667,7 @@
 
                     default:
                     {
-                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                        JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'"));
                     }
                 }
             }
@@ -11296,7 +11701,7 @@
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            throw std::domain_error("array index must not begin with '0'");
+                            JSON_THROW(std::domain_error("array index must not begin with '0'"));
                         }
 
                         // note: at performs range check
@@ -11306,7 +11711,7 @@
 
                     default:
                     {
-                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                        JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'"));
                     }
                 }
             }
@@ -11328,7 +11733,7 @@
             // check if nonempty reference string begins with slash
             if (reference_string[0] != '/')
             {
-                throw std::domain_error("JSON pointer must be empty or begin with '/'");
+                JSON_THROW(std::domain_error("JSON pointer must be empty or begin with '/'"));
             }
 
             // extract the reference tokens:
@@ -11336,7 +11741,7 @@
             // - start: position after the previous slash
             for (
                 // search for the first slash after the first character
-                size_t slash = reference_string.find_first_of("/", 1),
+                size_t slash = reference_string.find_first_of('/', 1),
                 // set the beginning of the first reference token
                 start = 1;
                 // we can stop if start == string::npos+1 = 0
@@ -11345,16 +11750,16 @@
                 // (will eventually be 0 if slash == std::string::npos)
                 start = slash + 1,
                 // find next slash
-                slash = reference_string.find_first_of("/", start))
+                slash = reference_string.find_first_of('/', start))
             {
                 // use the text between the beginning of the reference token
                 // (start) and the last slash (slash).
                 auto reference_token = reference_string.substr(start, slash - start);
 
                 // check reference tokens are properly escaped
-                for (size_t pos = reference_token.find_first_of("~");
+                for (size_t pos = reference_token.find_first_of('~');
                         pos != std::string::npos;
-                        pos = reference_token.find_first_of("~", pos + 1))
+                        pos = reference_token.find_first_of('~', pos + 1))
                 {
                     assert(reference_token[pos] == '~');
 
@@ -11363,7 +11768,7 @@
                             (reference_token[pos + 1] != '0' and
                              reference_token[pos + 1] != '1'))
                     {
-                        throw std::domain_error("escape error: '~' must be followed with '0' or '1'");
+                        JSON_THROW(std::domain_error("escape error: '~' must be followed with '0' or '1'"));
                     }
                 }
 
@@ -11489,7 +11894,7 @@
         {
             if (not value.is_object())
             {
-                throw std::domain_error("only objects can be unflattened");
+                JSON_THROW(std::domain_error("only objects can be unflattened"));
             }
 
             basic_json result;
@@ -11499,7 +11904,7 @@
             {
                 if (not element.second.is_primitive())
                 {
-                    throw std::domain_error("values in object must be primitive");
+                    JSON_THROW(std::domain_error("values in object must be primitive"));
                 }
 
                 // assign value to reference pointed to by JSON pointer; Note
@@ -11514,6 +11919,18 @@
         }
 
       private:
+        friend bool operator==(json_pointer const& lhs,
+                               json_pointer const& rhs) noexcept
+        {
+            return lhs.reference_tokens == rhs.reference_tokens;
+        }
+
+        friend bool operator!=(json_pointer const& lhs,
+                               json_pointer const& rhs) noexcept
+        {
+            return !(lhs == rhs);
+        }
+
         /// the reference tokens
         std::vector<std::string> reference_tokens {};
     };
@@ -11828,7 +12245,7 @@
                             if (static_cast<size_type>(idx) > parent.size())
                             {
                                 // avoid undefined behavior
-                                throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+                                JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range"));
                             }
                             else
                             {
@@ -11866,7 +12283,7 @@
                 }
                 else
                 {
-                    throw std::out_of_range("key '" + last_path + "' not found");
+                    JSON_THROW(std::out_of_range("key '" + last_path + "' not found"));
                 }
             }
             else if (parent.is_array())
@@ -11880,7 +12297,7 @@
         if (not json_patch.is_array())
         {
             // a JSON patch must be an array of objects
-            throw std::invalid_argument("JSON patch must be an array of objects");
+            JSON_THROW(std::invalid_argument("JSON patch must be an array of objects"));
         }
 
         // iterate and apply th eoperations
@@ -11900,13 +12317,13 @@
                 // check if desired value is present
                 if (it == val.m_value.object->end())
                 {
-                    throw std::invalid_argument(error_msg + " must have member '" + member + "'");
+                    JSON_THROW(std::invalid_argument(error_msg + " must have member '" + member + "'"));
                 }
 
                 // check if result is of type string
                 if (string_type and not it->second.is_string())
                 {
-                    throw std::invalid_argument(error_msg + " must have string member '" + member + "'");
+                    JSON_THROW(std::invalid_argument(error_msg + " must have string member '" + member + "'"));
                 }
 
                 // no error: return value
@@ -11916,7 +12333,7 @@
             // type check
             if (not val.is_object())
             {
-                throw std::invalid_argument("JSON patch must be an array of objects");
+                JSON_THROW(std::invalid_argument("JSON patch must be an array of objects"));
             }
 
             // collect mandatory members
@@ -11975,13 +12392,13 @@
                 case patch_operations::test:
                 {
                     bool success = false;
-                    try
+                    JSON_TRY
                     {
                         // check if "value" matches the one at "path"
                         // the "path" location must exist - use at()
                         success = (result.at(ptr) == get_value("test", "value", false));
                     }
-                    catch (std::out_of_range&)
+                    JSON_CATCH (std::out_of_range&)
                     {
                         // ignore out of range errors: success remains false
                     }
@@ -11989,7 +12406,7 @@
                     // throw an exception if test fails
                     if (not success)
                     {
-                        throw std::domain_error("unsuccessful: " + val.dump());
+                        JSON_THROW(std::domain_error("unsuccessful: " + val.dump()));
                     }
 
                     break;
@@ -11999,7 +12416,7 @@
                 {
                     // op must be "add", "remove", "replace", "move", "copy", or
                     // "test"
-                    throw std::invalid_argument("operation value '" + op + "' is invalid");
+                    JSON_THROW(std::invalid_argument("operation value '" + op + "' is invalid"));
                 }
             }
         }
@@ -12174,7 +12591,6 @@
     /// @}
 };
 
-
 /////////////
 // presets //
 /////////////
@@ -12188,7 +12604,7 @@
 @since version 1.0.0
 */
 using json = basic_json<>;
-}
+} // namespace nlohmann
 
 
 ///////////////////////
@@ -12229,7 +12645,7 @@
         return h(j.dump());
     }
 };
-}
+} // namespace std
 
 /*!
 @brief user-defined string literal for JSON values
@@ -12272,4 +12688,10 @@
     #pragma GCC diagnostic pop
 #endif
 
+// clean up
+#undef JSON_THROW
+#undef JSON_TRY
+#undef JSON_CATCH
+#undef JSON_DEPRECATED
+
 #endif
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index e1a43b5..068b876 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -39,6 +39,7 @@
 #include <cstdint> // int64_t, uint64_t
 #include <cstdlib> // strtod, strtof, strtold, strtoul
 #include <cstring> // strlen
+#include <forward_list> // forward_list
 #include <functional> // function, hash, less
 #include <initializer_list> // initializer_list
 #include <iomanip> // setw
@@ -58,13 +59,11 @@
 
 // exclude unsupported compilers
 #if defined(__clang__)
-    #define CLANG_VERSION (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__)
-    #if CLANG_VERSION < 30400
+    #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400
         #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers"
     #endif
 #elif defined(__GNUC__)
-    #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
-    #if GCC_VERSION < 40900
+    #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40900
         #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers"
     #endif
 #endif
@@ -90,6 +89,17 @@
     #define JSON_DEPRECATED
 #endif
 
+// allow to disable exceptions
+#if not defined(JSON_NOEXCEPTION) || defined(__EXCEPTIONS)
+    #define JSON_THROW(exception) throw exception
+    #define JSON_TRY try
+    #define JSON_CATCH(exception) catch(exception)
+#else
+    #define JSON_THROW(exception) std::abort()
+    #define JSON_TRY if(true)
+    #define JSON_CATCH(exception) if(false)
+#endif
+
 /*!
 @brief namespace for Niels Lohmann
 @see https://github.com/nlohmann
@@ -98,38 +108,841 @@
 namespace nlohmann
 {
 
-
 /*!
 @brief unnamed namespace with internal helper functions
+
+This namespace collects some functions that could not be defined inside the
+@ref basic_json class.
+
+@since version 2.1.0
+*/
+namespace detail
+{
+///////////////////////////
+// JSON type enumeration //
+///////////////////////////
+
+/*!
+@brief the JSON type enumeration
+
+This enumeration collects the different JSON types. It is internally used to
+distinguish the stored values, and the functions @ref basic_json::is_null(),
+@ref basic_json::is_object(), @ref basic_json::is_array(),
+@ref basic_json::is_string(), @ref basic_json::is_boolean(),
+@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),
+@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),
+@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and
+@ref basic_json::is_structured() rely on it.
+
+@note There are three enumeration entries (number_integer, number_unsigned, and
+number_float), because the library distinguishes these three types for numbers:
+@ref basic_json::number_unsigned_t is used for unsigned integers,
+@ref basic_json::number_integer_t is used for signed integers, and
+@ref basic_json::number_float_t is used for floating-point numbers or to
+approximate integers which do not fit in the limits of their respective type.
+
+@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON
+value with the default value for a given type
+
 @since version 1.0.0
 */
-namespace
+enum class value_t : uint8_t
 {
+    null,            ///< null value
+    object,          ///< object (unordered set of name/value pairs)
+    array,           ///< array (ordered collection of values)
+    string,          ///< string value
+    boolean,         ///< boolean value
+    number_integer,  ///< number value (signed integer)
+    number_unsigned, ///< number value (unsigned integer)
+    number_float,    ///< number value (floating-point)
+    discarded        ///< discarded by the the parser callback function
+};
+
+/*!
+@brief comparison operator for JSON types
+
+Returns an ordering that is similar to Python:
+- order: null < boolean < number < object < array < string
+- furthermore, each type is not smaller than itself
+
+@since version 1.0.0
+*/
+inline bool operator<(const value_t lhs, const value_t rhs) noexcept
+{
+    static constexpr std::array<uint8_t, 8> order = {{
+            0, // null
+            3, // object
+            4, // array
+            5, // string
+            1, // boolean
+            2, // integer
+            2, // unsigned
+            2, // float
+        }
+    };
+
+    // discarded values are not comparable
+    if (lhs == value_t::discarded or rhs == value_t::discarded)
+    {
+        return false;
+    }
+
+    return order[static_cast<std::size_t>(lhs)] <
+           order[static_cast<std::size_t>(rhs)];
+}
+
+
+/////////////
+// helpers //
+/////////////
+
+// alias templates to reduce boilerplate
+template<bool B, typename T = void>
+using enable_if_t = typename std::enable_if<B, T>::type;
+
+template<typename T>
+using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+
+// taken from http://stackoverflow.com/a/26936864/266378
+template<typename T>
+using is_unscoped_enum =
+    std::integral_constant<bool, std::is_convertible<T, int>::value and
+    std::is_enum<T>::value>;
+
+/*
+Implementation of two C++17 constructs: conjunction, negation. This is needed
+to avoid evaluating all the traits in a condition
+
+For example: not std::is_same<void, T>::value and has_value_type<T>::value
+will not compile when T = void (on MSVC at least). Whereas
+conjunction<negation<std::is_same<void, T>>, has_value_type<T>>::value will
+stop evaluating if negation<...>::value == false
+
+Please note that those constructs must be used with caution, since symbols can
+become very long quickly (which can slow down compilation and cause MSVC
+internal compiler errors). Only use it when you have to (see example ahead).
+*/
+template<class...> struct conjunction : std::true_type {};
+template<class B1> struct conjunction<B1> : B1 {};
+template<class B1, class... Bn>
+struct conjunction<B1, Bn...> : std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};
+
+template<class B> struct negation : std::integral_constant < bool, !B::value > {};
+
+// dispatch utility (taken from ranges-v3)
+template<unsigned N> struct priority_tag : priority_tag < N - 1 > {};
+template<> struct priority_tag<0> {};
+
+
+//////////////////
+// constructors //
+//////////////////
+
+template<value_t> struct external_constructor;
+
+template<>
+struct external_constructor<value_t::boolean>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
+    {
+        j.m_type = value_t::boolean;
+        j.m_value = b;
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::string>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
+    {
+        j.m_type = value_t::string;
+        j.m_value = s;
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::number_float>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
+    {
+        // replace infinity and NAN by null
+        if (not std::isfinite(val))
+        {
+            j = BasicJsonType{};
+        }
+        else
+        {
+            j.m_type = value_t::number_float;
+            j.m_value = val;
+        }
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::number_unsigned>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
+    {
+        j.m_type = value_t::number_unsigned;
+        j.m_value = val;
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::number_integer>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
+    {
+        j.m_type = value_t::number_integer;
+        j.m_value = val;
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::array>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
+    {
+        j.m_type = value_t::array;
+        j.m_value = arr;
+        j.assert_invariant();
+    }
+
+    template<typename BasicJsonType, typename CompatibleArrayType,
+             enable_if_t<not std::is_same<CompatibleArrayType,
+                                          typename BasicJsonType::array_t>::value,
+                         int> = 0>
+    static void construct(BasicJsonType& j, const CompatibleArrayType& arr)
+    {
+        using std::begin;
+        using std::end;
+        j.m_type = value_t::array;
+        j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::object>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
+    {
+        j.m_type = value_t::object;
+        j.m_value = obj;
+        j.assert_invariant();
+    }
+
+    template<typename BasicJsonType, typename CompatibleObjectType,
+             enable_if_t<not std::is_same<CompatibleObjectType,
+                                          typename BasicJsonType::object_t>::value,
+                         int> = 0>
+    static void construct(BasicJsonType& j, const CompatibleObjectType& obj)
+    {
+        using std::begin;
+        using std::end;
+
+        j.m_type = value_t::object;
+        j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
+        j.assert_invariant();
+    }
+};
+
+
+////////////////////////
+// has_/is_ functions //
+////////////////////////
+
 /*!
 @brief Helper to determine whether there's a key_type for T.
 
-Thus helper is used to tell associative containers apart from other containers
+This helper is used to tell associative containers apart from other containers
 such as sequence containers. For instance, `std::map` passes the test as it
 contains a `mapped_type`, whereas `std::vector` fails the test.
 
 @sa http://stackoverflow.com/a/7728728/266378
 @since version 1.0.0, overworked in version 2.0.6
 */
-template<typename T>
-struct has_mapped_type
-{
-  private:
-    template <typename U, typename = typename U::mapped_type>
-    static int detect(U&&);
+#define NLOHMANN_JSON_HAS_HELPER(type)                                        \
+    template<typename T> struct has_##type {                                  \
+    private:                                                                  \
+        template<typename U, typename = typename U::type>                     \
+        static int detect(U &&);                                              \
+        static void detect(...);                                              \
+    public:                                                                   \
+        static constexpr bool value =                                         \
+                std::is_integral<decltype(detect(std::declval<T>()))>::value; \
+    }
 
-    static void detect(...);
-  public:
-    static constexpr bool value =
-        std::is_integral<decltype(detect(std::declval<T>()))>::value;
+NLOHMANN_JSON_HAS_HELPER(mapped_type);
+NLOHMANN_JSON_HAS_HELPER(key_type);
+NLOHMANN_JSON_HAS_HELPER(value_type);
+NLOHMANN_JSON_HAS_HELPER(iterator);
+
+#undef NLOHMANN_JSON_HAS_HELPER
+
+
+template<bool B, class RealType, class CompatibleObjectType>
+struct is_compatible_object_type_impl : std::false_type {};
+
+template<class RealType, class CompatibleObjectType>
+struct is_compatible_object_type_impl<true, RealType, CompatibleObjectType>
+{
+    static constexpr auto value =
+        std::is_constructible<typename RealType::key_type,
+        typename CompatibleObjectType::key_type>::value and
+        std::is_constructible<typename RealType::mapped_type,
+        typename CompatibleObjectType::mapped_type>::value;
 };
 
+template<class BasicJsonType, class CompatibleObjectType>
+struct is_compatible_object_type
+{
+    static auto constexpr value = is_compatible_object_type_impl <
+                                  conjunction<negation<std::is_same<void, CompatibleObjectType>>,
+                                  has_mapped_type<CompatibleObjectType>,
+                                  has_key_type<CompatibleObjectType>>::value,
+                                  typename BasicJsonType::object_t, CompatibleObjectType >::value;
+};
+
+template<typename BasicJsonType, typename T>
+struct is_basic_json_nested_type
+{
+    static auto constexpr value = std::is_same<T, typename BasicJsonType::iterator>::value or
+                                  std::is_same<T, typename BasicJsonType::const_iterator>::value or
+                                  std::is_same<T, typename BasicJsonType::reverse_iterator>::value or
+                                  std::is_same<T, typename BasicJsonType::const_reverse_iterator>::value or
+                                  std::is_same<T, typename BasicJsonType::json_pointer>::value;
+};
+
+template<class BasicJsonType, class CompatibleArrayType>
+struct is_compatible_array_type
+{
+    static auto constexpr value =
+        conjunction<negation<std::is_same<void, CompatibleArrayType>>,
+        negation<is_compatible_object_type<
+        BasicJsonType, CompatibleArrayType>>,
+        negation<std::is_constructible<typename BasicJsonType::string_t,
+        CompatibleArrayType>>,
+        negation<is_basic_json_nested_type<BasicJsonType, CompatibleArrayType>>,
+        has_value_type<CompatibleArrayType>,
+        has_iterator<CompatibleArrayType>>::value;
+};
+
+template<bool, typename, typename>
+struct is_compatible_integer_type_impl : std::false_type {};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType>
+struct is_compatible_integer_type_impl<true, RealIntegerType, CompatibleNumberIntegerType>
+{
+    // is there an assert somewhere on overflows?
+    using RealLimits = std::numeric_limits<RealIntegerType>;
+    using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;
+
+    static constexpr auto value =
+        std::is_constructible<RealIntegerType,
+        CompatibleNumberIntegerType>::value and
+        CompatibleLimits::is_integer and
+        RealLimits::is_signed == CompatibleLimits::is_signed;
+};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType>
+struct is_compatible_integer_type
+{
+    static constexpr auto value =
+        is_compatible_integer_type_impl <
+        std::is_integral<CompatibleNumberIntegerType>::value and
+        not std::is_same<bool, CompatibleNumberIntegerType>::value,
+        RealIntegerType, CompatibleNumberIntegerType > ::value;
+};
+
+
+// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
+template<typename BasicJsonType, typename T>
+struct has_from_json
+{
+  private:
+    // also check the return type of from_json
+    template<typename U, typename = enable_if_t<std::is_same<void, decltype(uncvref_t<U>::from_json(
+                 std::declval<BasicJsonType>(), std::declval<T&>()))>::value>>
+    static int detect(U&&);
+    static void detect(...);
+
+  public:
+    static constexpr bool value = std::is_integral<decltype(
+                                      detect(std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
+};
+
+// This trait checks if JSONSerializer<T>::from_json(json const&) exists
+// this overload is used for non-default-constructible user-defined-types
+template<typename BasicJsonType, typename T>
+struct has_non_default_from_json
+{
+  private:
+    template <
+        typename U,
+        typename = enable_if_t<std::is_same<
+                                   T, decltype(uncvref_t<U>::from_json(std::declval<BasicJsonType>()))>::value >>
+    static int detect(U&&);
+    static void detect(...);
+
+  public:
+    static constexpr bool value = std::is_integral<decltype(detect(
+                                      std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
+};
+
+// This trait checks if BasicJsonType::json_serializer<T>::to_json exists
+template<typename BasicJsonType, typename T>
+struct has_to_json
+{
+  private:
+    template<typename U, typename = decltype(uncvref_t<U>::to_json(
+                 std::declval<BasicJsonType&>(), std::declval<T>()))>
+    static int detect(U&&);
+    static void detect(...);
+
+  public:
+    static constexpr bool value = std::is_integral<decltype(detect(
+                                      std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
+};
+
+
+/////////////
+// to_json //
+/////////////
+
+template<typename BasicJsonType>
+void to_json(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
+{
+    external_constructor<value_t::boolean>::construct(j, b);
 }
 
+template<typename BasicJsonType, typename CompatibleString,
+         enable_if_t<std::is_constructible<typename BasicJsonType::string_t,
+                     CompatibleString>::value, int> = 0>
+void to_json(BasicJsonType& j, const CompatibleString& s)
+{
+    external_constructor<value_t::string>::construct(j, s);
+}
+
+template<typename BasicJsonType, typename FloatType,
+         enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>
+void to_json(BasicJsonType& j, FloatType val) noexcept
+{
+    external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));
+}
+
+template <
+    typename BasicJsonType, typename CompatibleNumberUnsignedType,
+    enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t,
+                CompatibleNumberUnsignedType>::value, int> = 0 >
+void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
+{
+    external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
+}
+
+template <
+    typename BasicJsonType, typename CompatibleNumberIntegerType,
+    enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t,
+                CompatibleNumberIntegerType>::value, int> = 0 >
+void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
+{
+    external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
+}
+
+template<typename BasicJsonType, typename UnscopedEnumType,
+         enable_if_t<is_unscoped_enum<UnscopedEnumType>::value, int> = 0>
+void to_json(BasicJsonType& j, UnscopedEnumType e) noexcept
+{
+    external_constructor<value_t::number_integer>::construct(j, e);
+}
+
+template <
+    typename BasicJsonType, typename CompatibleArrayType,
+    enable_if_t <
+        is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value or
+        std::is_same<typename BasicJsonType::array_t, CompatibleArrayType>::value,
+        int > = 0 >
+void to_json(BasicJsonType& j, const  CompatibleArrayType& arr)
+{
+    external_constructor<value_t::array>::construct(j, arr);
+}
+
+template <
+    typename BasicJsonType, typename CompatibleObjectType,
+    enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value,
+                int> = 0 >
+void to_json(BasicJsonType& j, const  CompatibleObjectType& arr)
+{
+    external_constructor<value_t::object>::construct(j, arr);
+}
+
+
+///////////////
+// from_json //
+///////////////
+
+// overloads for basic_json template parameters
+template<typename BasicJsonType, typename ArithmeticType,
+         enable_if_t<std::is_arithmetic<ArithmeticType>::value and
+                     not std::is_same<ArithmeticType,
+                                      typename BasicJsonType::boolean_t>::value,
+                     int> = 0>
+void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
+{
+    switch (static_cast<value_t>(j))
+    {
+        case value_t::number_unsigned:
+        {
+            val = static_cast<ArithmeticType>(
+                      *j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
+            break;
+        }
+        case value_t::number_integer:
+        {
+            val = static_cast<ArithmeticType>(
+                      *j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
+            break;
+        }
+        case value_t::number_float:
+        {
+            val = static_cast<ArithmeticType>(
+                      *j.template get_ptr<const typename BasicJsonType::number_float_t*>());
+            break;
+        }
+        default:
+        {
+            JSON_THROW(
+                std::domain_error("type must be number, but is " + j.type_name()));
+        }
+    }
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
+{
+    if (not j.is_boolean())
+    {
+        JSON_THROW(std::domain_error("type must be boolean, but is " + j.type_name()));
+    }
+    b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
+{
+    if (not j.is_string())
+    {
+        JSON_THROW(std::domain_error("type must be string, but is " + j.type_name()));
+    }
+    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
+{
+    get_arithmetic_value(j, val);
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
+{
+    get_arithmetic_value(j, val);
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
+{
+    get_arithmetic_value(j, val);
+}
+
+template<typename BasicJsonType, typename UnscopedEnumType,
+         enable_if_t<is_unscoped_enum<UnscopedEnumType>::value, int> = 0>
+void from_json(const BasicJsonType& j, UnscopedEnumType& e)
+{
+    typename std::underlying_type<UnscopedEnumType>::type val = e;
+    get_arithmetic_value(j, val);
+    e = static_cast<UnscopedEnumType>(val);
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::array_t& arr)
+{
+    if (not j.is_array())
+    {
+        JSON_THROW(std::domain_error("type must be array, but is " + j.type_name()));
+    }
+    arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
+}
+
+// forward_list doesn't have an insert method
+template<typename BasicJsonType, typename T, typename Allocator>
+void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
+{
+    // do not perform the check when user wants to retrieve jsons
+    // (except when it's null.. ?)
+    if (j.is_null())
+    {
+        JSON_THROW(std::domain_error("type must be array, but is " + j.type_name()));
+    }
+    if (not std::is_same<T, BasicJsonType>::value)
+    {
+        if (not j.is_array())
+        {
+            JSON_THROW(std::domain_error("type must be array, but is " + j.type_name()));
+        }
+    }
+    for (auto it = j.rbegin(), end = j.rend(); it != end; ++it)
+    {
+        l.push_front(it->template get<T>());
+    }
+}
+
+template<typename BasicJsonType, typename CompatibleArrayType>
+void from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<0>)
+{
+    using std::begin;
+    using std::end;
+
+    std::transform(j.begin(), j.end(),
+                   std::inserter(arr, end(arr)), [](const BasicJsonType & i)
+    {
+        // get<BasicJsonType>() returns *this, this won't call a from_json
+        // method when value_type is BasicJsonType
+        return i.template get<typename CompatibleArrayType::value_type>();
+    });
+}
+
+template<typename BasicJsonType, typename CompatibleArrayType>
+auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<1>)
+-> decltype(
+    arr.reserve(std::declval<typename CompatibleArrayType::size_type>()),
+    void())
+{
+    using std::begin;
+    using std::end;
+
+    arr.reserve(j.size());
+    std::transform(
+        j.begin(), j.end(), std::inserter(arr, end(arr)), [](const BasicJsonType & i)
+    {
+        // get<BasicJsonType>() returns *this, this won't call a from_json
+        // method when value_type is BasicJsonType
+        return i.template get<typename CompatibleArrayType::value_type>();
+    });
+}
+
+template<typename BasicJsonType, typename CompatibleArrayType,
+         enable_if_t<is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value and
+                     not std::is_same<typename BasicJsonType::array_t, CompatibleArrayType>::value, int> = 0>
+void from_json(const BasicJsonType& j, CompatibleArrayType& arr)
+{
+    if (j.is_null())
+    {
+        JSON_THROW(std::domain_error("type must be array, but is " + j.type_name()));
+    }
+
+    // when T == BasicJsonType, do not check if value_t is correct
+    if (not std::is_same<typename CompatibleArrayType::value_type, BasicJsonType>::value)
+    {
+        if (not j.is_array())
+        {
+            JSON_THROW(std::domain_error("type must be array, but is " + j.type_name()));
+        }
+    }
+    from_json_array_impl(j, arr, priority_tag<1> {});
+}
+
+template<typename BasicJsonType, typename CompatibleObjectType,
+         enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value, int> = 0>
+void from_json(const BasicJsonType& j, CompatibleObjectType& obj)
+{
+    if (not j.is_object())
+    {
+        JSON_THROW(std::domain_error("type must be object, but is " + j.type_name()));
+    }
+
+    auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
+    using std::begin;
+    using std::end;
+    // we could avoid the assignment, but this might require a for loop, which
+    // might be less efficient than the container constructor for some
+    // containers (would it?)
+    obj = CompatibleObjectType(begin(*inner_object), end(*inner_object));
+}
+
+// overload for arithmetic types, not chosen for basic_json template arguments
+// (BooleanType, etc..); note: Is it really necessary to provide explicit
+// overloads for boolean_t etc. in case of a custom BooleanType which is not
+// an arithmetic type?
+template<typename BasicJsonType, typename ArithmeticType,
+         enable_if_t <
+             std::is_arithmetic<ArithmeticType>::value and
+             not std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value and
+             not std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value and
+             not std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value and
+             not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
+             int> = 0>
+void from_json(const BasicJsonType& j, ArithmeticType& val)
+{
+    switch (static_cast<value_t>(j))
+    {
+        case value_t::number_unsigned:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
+            break;
+        }
+        case value_t::number_integer:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
+            break;
+        }
+        case value_t::number_float:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
+            break;
+        }
+        case value_t::boolean:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
+            break;
+        }
+        default:
+        {
+            JSON_THROW(std::domain_error("type must be number, but is " + j.type_name()));
+        }
+    }
+}
+
+struct to_json_fn
+{
+  private:
+    template<typename BasicJsonType, typename T>
+    auto call(BasicJsonType& j, T&& val, priority_tag<1>) const noexcept(noexcept(to_json(j, std::forward<T>(val))))
+    -> decltype(to_json(j, std::forward<T>(val)), void())
+    {
+        return to_json(j, std::forward<T>(val));
+    }
+
+    template<typename BasicJsonType, typename T>
+    void call(BasicJsonType&, T&&, priority_tag<0>) const noexcept
+    {
+        static_assert(sizeof(BasicJsonType) == 0,
+                      "could not find to_json() method in T's namespace");
+    }
+
+  public:
+    template<typename BasicJsonType, typename T>
+    void operator()(BasicJsonType& j, T&& val) const
+    noexcept(noexcept(std::declval<to_json_fn>().call(j, std::forward<T>(val), priority_tag<1> {})))
+    {
+        return call(j, std::forward<T>(val), priority_tag<1> {});
+    }
+};
+
+struct from_json_fn
+{
+  private:
+    template<typename BasicJsonType, typename T>
+    auto call(const BasicJsonType& j, T& val, priority_tag<1>) const
+    noexcept(noexcept(from_json(j, val)))
+    -> decltype(from_json(j, val), void())
+    {
+        return from_json(j, val);
+    }
+
+    template<typename BasicJsonType, typename T>
+    void call(const BasicJsonType&, T&, priority_tag<0>) const noexcept
+    {
+        static_assert(sizeof(BasicJsonType) == 0,
+                      "could not find from_json() method in T's namespace");
+    }
+
+  public:
+    template<typename BasicJsonType, typename T>
+    void operator()(const BasicJsonType& j, T& val) const
+    noexcept(noexcept(std::declval<from_json_fn>().call(j, val, priority_tag<1> {})))
+    {
+        return call(j, val, priority_tag<1> {});
+    }
+};
+
+// taken from ranges-v3
+template<typename T>
+struct static_const
+{
+    static constexpr T value{};
+};
+
+template<typename T>
+constexpr T static_const<T>::value;
+} // namespace detail
+
+
+/// namespace to hold default `to_json` / `from_json` functions
+namespace
+{
+constexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value;
+constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value;
+}
+
+
+/*!
+@brief default JSONSerializer template argument
+
+This serializer ignores the template arguments and uses ADL
+([argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adl))
+for serialization.
+*/
+template<typename = void, typename = void>
+struct adl_serializer
+{
+    /*!
+    @brief convert a JSON value to any value type
+
+    This function is usually called by the `get()` function of the
+    @ref basic_json class (either explicit or via conversion operators).
+
+    @param[in] j         JSON value to read from
+    @param[in, out] val  value to write to
+    */
+    template<typename BasicJsonType, typename ValueType>
+    static void from_json(BasicJsonType&& j, ValueType& val) noexcept(
+        noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))
+    {
+        ::nlohmann::from_json(std::forward<BasicJsonType>(j), val);
+    }
+
+    /*!
+    @brief convert any value type to a JSON value
+
+    This function is usually called by the constructors of the @ref basic_json
+    class.
+
+    @param[in, out] j  JSON value to write to
+    @param[in] val     value to read from
+    */
+    template<typename BasicJsonType, typename ValueType>
+    static void to_json(BasicJsonType& j, ValueType&& val) noexcept(
+        noexcept(::nlohmann::to_json(j, std::forward<ValueType>(val))))
+    {
+        ::nlohmann::to_json(j, std::forward<ValueType>(val));
+    }
+};
+
+
 /*!
 @brief a class to store JSON values
 
@@ -149,11 +962,14 @@
 default; will be used in @ref number_float_t)
 @tparam AllocatorType type of the allocator to use (`std::allocator` by
 default)
+@tparam JSONSerializer the serializer to resolve internal calls to `to_json()`
+and `from_json()` (@ref adl_serializer by default)
 
 @requirement The class satisfies the following concept requirements:
 - Basic
  - [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible):
-   JSON values can be default constructed. The result will be a JSON null value.
+   JSON values can be default constructed. The result will be a JSON null
+   value.
  - [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible):
    A JSON value can be constructed from an rvalue argument.
  - [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible):
@@ -168,8 +984,8 @@
  - [StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType):
    JSON values have
    [standard layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout):
-   All non-static data members are private and standard layout types, the class
-   has no virtual functions or (virtual) base classes.
+   All non-static data members are private and standard layout types, the
+   class has no virtual functions or (virtual) base classes.
 - Library-wide
  - [EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable):
    JSON values can be compared with `==`, see @ref
@@ -216,21 +1032,26 @@
     class NumberIntegerType = std::int64_t,
     class NumberUnsignedType = std::uint64_t,
     class NumberFloatType = double,
-    template<typename U> class AllocatorType = std::allocator
+    template<typename U> class AllocatorType = std::allocator,
+    template<typename T, typename SFINAE = void> class JSONSerializer = adl_serializer
     >
 class basic_json
 {
   private:
+    template<detail::value_t> friend struct detail::external_constructor;
     /// workaround type for MSVC
     using basic_json_t = basic_json<ObjectType, ArrayType, StringType,
           BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType,
-          AllocatorType>;
+          AllocatorType, JSONSerializer>;
 
   public:
+    using value_t = detail::value_t;
     // forward declarations
     template<typename U> class iter_impl;
     template<typename Base> class json_reverse_iterator;
     class json_pointer;
+    template<typename T, typename SFINAE>
+    using json_serializer = JSONSerializer<T, SFINAE>;
 
     /////////////////////
     // container types //
@@ -282,6 +1103,84 @@
         return allocator_type();
     }
 
+    /*!
+    @brief returns version information on the library
+
+    This function returns a JSON object with infiormation about the library,
+    including the version number and information on the platform and compiler.
+
+    @return JSON object holding version information
+    key         | description
+    ----------- | ---------------
+    `compiler`  | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version).
+    `copyright` | The copyright line for the library as string.
+    `name`      | The name of the library as string.
+    `platform`  | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`.
+    `url`       | The URL of the project as string.
+    `version`   | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string).
+
+    @liveexample{The following code shows an example output of the `meta()`
+    function.,meta}
+
+    @complexity Constant.
+
+    @since 2.1.0
+    */
+    static basic_json meta()
+    {
+        basic_json result;
+
+        result["copyright"] = "(C) 2013-2017 Niels Lohmann";
+        result["name"] = "JSON for Modern C++";
+        result["url"] = "https://github.com/nlohmann/json";
+        result["version"] =
+        {
+            {"string", "2.1.0"},
+            {"major", 2},
+            {"minor", 1},
+            {"patch", 0},
+        };
+
+#ifdef _WIN32
+        result["platform"] = "win32";
+#elif defined __linux__
+        result["platform"] = "linux";
+#elif defined __APPLE__
+        result["platform"] = "apple";
+#elif defined __unix__
+        result["platform"] = "unix";
+#else
+        result["platform"] = "unknown";
+#endif
+
+#if defined(__clang__)
+        result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}};
+#elif defined(__ICC) || defined(__INTEL_COMPILER)
+        result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}};
+#elif defined(__GNUC__) || defined(__GNUG__)
+        result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}};
+#elif defined(__HP_cc) || defined(__HP_aCC)
+        result["compiler"] = "hp"
+#elif defined(__IBMCPP__)
+        result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}};
+#elif defined(_MSC_VER)
+        result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}};
+#elif defined(__PGI)
+        result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}};
+#elif defined(__SUNPRO_CC)
+        result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}};
+#else
+        result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}};
+#endif
+
+#ifdef __cplusplus
+        result["compiler"]["c++"] = std::to_string(__cplusplus);
+#else
+        result["compiler"]["c++"] = "unknown";
+#endif
+        return result;
+    }
+
 
     ///////////////////////////
     // JSON value data types //
@@ -449,6 +1348,12 @@
     std::string
     @endcode
 
+    #### Encoding
+
+    Strings are stored in UTF-8 encoding. Therefore, functions like
+    `std::string::size()` or `std::string::length()` return the number of
+    bytes in the string rather than the number of characters or glyphs.
+
     #### String comparison
 
     [RFC 7159](http://rfc7159.net/rfc7159) states:
@@ -713,47 +1618,6 @@
 
     /// @}
 
-
-    ///////////////////////////
-    // JSON type enumeration //
-    ///////////////////////////
-
-    /*!
-    @brief the JSON type enumeration
-
-    This enumeration collects the different JSON types. It is internally used
-    to distinguish the stored values, and the functions @ref is_null(), @ref
-    is_object(), @ref is_array(), @ref is_string(), @ref is_boolean(), @ref
-    is_number() (with @ref is_number_integer(), @ref is_number_unsigned(), and
-    @ref is_number_float()), @ref is_discarded(), @ref is_primitive(), and
-    @ref is_structured() rely on it.
-
-    @note There are three enumeration entries (number_integer,
-    number_unsigned, and number_float), because the library distinguishes
-    these three types for numbers: @ref number_unsigned_t is used for unsigned
-    integers, @ref number_integer_t is used for signed integers, and @ref
-    number_float_t is used for floating-point numbers or to approximate
-    integers which do not fit in the limits of their respective type.
-
-    @sa @ref basic_json(const value_t value_type) -- create a JSON value with
-    the default value for a given type
-
-    @since version 1.0.0
-    */
-    enum class value_t : uint8_t
-    {
-        null,            ///< null value
-        object,          ///< object (unordered set of name/value pairs)
-        array,           ///< array (ordered collection of values)
-        string,          ///< string value
-        boolean,         ///< boolean value
-        number_integer,  ///< number value (signed integer)
-        number_unsigned, ///< number value (unsigned integer)
-        number_float,    ///< number value (floating-point)
-        discarded        ///< discarded by the the parser callback function
-    };
-
-
   private:
 
     /// helper for exception-safe object creation
@@ -767,7 +1631,7 @@
         };
         std::unique_ptr<T, decltype(deleter)> object(alloc.allocate(1), deleter);
         alloc.construct(object.get(), std::forward<Args>(args)...);
-        assert(object.get() != nullptr);
+        assert(object != nullptr);
         return object.release();
     }
 
@@ -882,7 +1746,7 @@
                 {
                     if (t == value_t::null)
                     {
-                        throw std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21 2.0.10"); // LCOV_EXCL_LINE
+                        JSON_THROW(std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21 2.1.0")); // LCOV_EXCL_LINE
                     }
                     break;
                 }
@@ -1046,18 +1910,6 @@
     @liveexample{The following code shows the constructor for different @ref
     value_t values,basic_json__value_t}
 
-    @sa @ref basic_json(std::nullptr_t) -- create a `null` value
-    @sa @ref basic_json(boolean_t value) -- create a boolean value
-    @sa @ref basic_json(const string_t&) -- create a string value
-    @sa @ref basic_json(const object_t&) -- create a object value
-    @sa @ref basic_json(const array_t&) -- create a array value
-    @sa @ref basic_json(const number_float_t) -- create a number
-    (floating-point) value
-    @sa @ref basic_json(const number_integer_t) -- create a number (integer)
-    value
-    @sa @ref basic_json(const number_unsigned_t) -- create a number (unsigned)
-    value
-
     @since version 1.0.0
     */
     basic_json(const value_t value_type)
@@ -1091,473 +1943,69 @@
     }
 
     /*!
-    @brief create an object (explicit)
+    @brief create a JSON value
 
-    Create an object JSON value with a given content.
+    This is a "catch all" constructor for all compatible JSON types; that is,
+    types for which a `to_json()` method exsits. The constructor forwards the
+    parameter @a val to that method (to `json_serializer<U>::to_json` method
+    with `U = uncvref_t<CompatibleType>`, to be exact).
 
-    @param[in] val  a value for the object
+    Template type @a CompatibleType includes, but is not limited to, the
+    following types:
+    - **arrays**: @ref array_t and all kinds of compatible containers such as
+      `std::vector`, `std::deque`, `std::list`, `std::forward_list`,
+      `std::array`, `std::set`, `std::unordered_set`, `std::multiset`, and
+      `unordered_multiset` with a `value_type` from which a @ref basic_json
+      value can be constructed.
+    - **objects**: @ref object_t and all kinds of compatible associative
+      containers such as `std::map`, `std::unordered_map`, `std::multimap`,
+      and `std::unordered_multimap` with a `key_type` compatible to
+      @ref string_t and a `value_type` from which a @ref basic_json value can
+      be constructed.
+    - **strings**: @ref string_t, string literals, and all compatible string
+      containers can be used.
+    - **numbers**: @ref number_integer_t, @ref number_unsigned_t,
+      @ref number_float_t, and all convertible number types such as `int`,
+      `size_t`, `int64_t`, `float` or `double` can be used.
+    - **boolean**: @ref boolean_t / `bool` can be used.
 
-    @complexity Linear in the size of the passed @a val.
+    See the examples below.
 
-    @throw std::bad_alloc if allocation for object value fails
+    @tparam CompatibleType a type such that:
+    - @a CompatibleType is not derived from `std::istream`,
+    - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move
+         constructors),
+    - @a CompatibleType is not a @ref basic_json nested type (e.g.,
+         @ref json_pointer, @ref iterator, etc ...)
+    - @ref @ref json_serializer<U> has a
+         `to_json(basic_json_t&, CompatibleType&&)` method
 
-    @liveexample{The following code shows the constructor with an @ref
-    object_t parameter.,basic_json__object_t}
+    @tparam U = `uncvref_t<CompatibleType>`
 
-    @sa @ref basic_json(const CompatibleObjectType&) -- create an object value
-    from a compatible STL container
+    @param[in] val the value to be forwarded
 
-    @since version 1.0.0
-    */
-    basic_json(const object_t& val)
-        : m_type(value_t::object), m_value(val)
-    {
-        assert_invariant();
-    }
+    @complexity Usually linear in the size of the passed @a val, also
+                depending on the implementation of the called `to_json()`
+                method.
 
-    /*!
-    @brief create an object (implicit)
-
-    Create an object JSON value with a given content. This constructor allows
-    any type @a CompatibleObjectType that can be used to construct values of
-    type @ref object_t.
-
-    @tparam CompatibleObjectType An object type whose `key_type` and
-    `value_type` is compatible to @ref object_t. Examples include `std::map`,
-    `std::unordered_map`, `std::multimap`, and `std::unordered_multimap` with
-    a `key_type` of `std::string`, and a `value_type` from which a @ref
-    basic_json value can be constructed.
-
-    @param[in] val  a value for the object
-
-    @complexity Linear in the size of the passed @a val.
-
-    @throw std::bad_alloc if allocation for object value fails
+    @throw what `json_serializer<U>::to_json()` throws
 
     @liveexample{The following code shows the constructor with several
-    compatible object type parameters.,basic_json__CompatibleObjectType}
+    compatible types.,basic_json__CompatibleType}
 
-    @sa @ref basic_json(const object_t&) -- create an object value
-
-    @since version 1.0.0
+    @since version 2.1.0
     */
-    template<class CompatibleObjectType, typename std::enable_if<
-                 std::is_constructible<typename object_t::key_type, typename CompatibleObjectType::key_type>::value and
-                 std::is_constructible<basic_json, typename CompatibleObjectType::mapped_type>::value, int>::type = 0>
-    basic_json(const CompatibleObjectType& val)
-        : m_type(value_t::object)
+    template<typename CompatibleType, typename U = detail::uncvref_t<CompatibleType>,
+             detail::enable_if_t<not std::is_base_of<std::istream, U>::value and
+                                 not std::is_same<U, basic_json_t>::value and
+                                 not detail::is_basic_json_nested_type<
+                                     basic_json_t, U>::value and
+                                 detail::has_to_json<basic_json, U>::value,
+                                 int> = 0>
+    basic_json(CompatibleType && val) noexcept(noexcept(JSONSerializer<U>::to_json(
+                std::declval<basic_json_t&>(), std::forward<CompatibleType>(val))))
     {
-        using std::begin;
-        using std::end;
-        m_value.object = create<object_t>(begin(val), end(val));
-        assert_invariant();
-    }
-
-    /*!
-    @brief create an array (explicit)
-
-    Create an array JSON value with a given content.
-
-    @param[in] val  a value for the array
-
-    @complexity Linear in the size of the passed @a val.
-
-    @throw std::bad_alloc if allocation for array value fails
-
-    @liveexample{The following code shows the constructor with an @ref array_t
-    parameter.,basic_json__array_t}
-
-    @sa @ref basic_json(const CompatibleArrayType&) -- create an array value
-    from a compatible STL containers
-
-    @since version 1.0.0
-    */
-    basic_json(const array_t& val)
-        : m_type(value_t::array), m_value(val)
-    {
-        assert_invariant();
-    }
-
-    /*!
-    @brief create an array (implicit)
-
-    Create an array JSON value with a given content. This constructor allows
-    any type @a CompatibleArrayType that can be used to construct values of
-    type @ref array_t.
-
-    @tparam CompatibleArrayType An object type whose `value_type` is
-    compatible to @ref array_t. Examples include `std::vector`, `std::deque`,
-    `std::list`, `std::forward_list`, `std::array`, `std::set`,
-    `std::unordered_set`, `std::multiset`, and `unordered_multiset` with a
-    `value_type` from which a @ref basic_json value can be constructed.
-
-    @param[in] val  a value for the array
-
-    @complexity Linear in the size of the passed @a val.
-
-    @throw std::bad_alloc if allocation for array value fails
-
-    @liveexample{The following code shows the constructor with several
-    compatible array type parameters.,basic_json__CompatibleArrayType}
-
-    @sa @ref basic_json(const array_t&) -- create an array value
-
-    @since version 1.0.0
-    */
-    template<class CompatibleArrayType, typename std::enable_if<
-                 not std::is_same<CompatibleArrayType, typename basic_json_t::iterator>::value and
-                 not std::is_same<CompatibleArrayType, typename basic_json_t::const_iterator>::value and
-                 not std::is_same<CompatibleArrayType, typename basic_json_t::reverse_iterator>::value and
-                 not std::is_same<CompatibleArrayType, typename basic_json_t::const_reverse_iterator>::value and
-                 not std::is_same<CompatibleArrayType, typename array_t::iterator>::value and
-                 not std::is_same<CompatibleArrayType, typename array_t::const_iterator>::value and
-                 std::is_constructible<basic_json, typename CompatibleArrayType::value_type>::value, int>::type = 0>
-    basic_json(const CompatibleArrayType& val)
-        : m_type(value_t::array)
-    {
-        using std::begin;
-        using std::end;
-        m_value.array = create<array_t>(begin(val), end(val));
-        assert_invariant();
-    }
-
-    /*!
-    @brief create a string (explicit)
-
-    Create an string JSON value with a given content.
-
-    @param[in] val  a value for the string
-
-    @complexity Linear in the size of the passed @a val.
-
-    @throw std::bad_alloc if allocation for string value fails
-
-    @liveexample{The following code shows the constructor with an @ref
-    string_t parameter.,basic_json__string_t}
-
-    @sa @ref basic_json(const typename string_t::value_type*) -- create a
-    string value from a character pointer
-    @sa @ref basic_json(const CompatibleStringType&) -- create a string value
-    from a compatible string container
-
-    @since version 1.0.0
-    */
-    basic_json(const string_t& val)
-        : m_type(value_t::string), m_value(val)
-    {
-        assert_invariant();
-    }
-
-    /*!
-    @brief create a string (explicit)
-
-    Create a string JSON value with a given content.
-
-    @param[in] val  a literal value for the string
-
-    @complexity Linear in the size of the passed @a val.
-
-    @throw std::bad_alloc if allocation for string value fails
-
-    @liveexample{The following code shows the constructor with string literal
-    parameter.,basic_json__string_t_value_type}
-
-    @sa @ref basic_json(const string_t&) -- create a string value
-    @sa @ref basic_json(const CompatibleStringType&) -- create a string value
-    from a compatible string container
-
-    @since version 1.0.0
-    */
-    basic_json(const typename string_t::value_type* val)
-        : basic_json(string_t(val))
-    {
-        assert_invariant();
-    }
-
-    /*!
-    @brief create a string (implicit)
-
-    Create a string JSON value with a given content.
-
-    @param[in] val  a value for the string
-
-    @tparam CompatibleStringType an string type which is compatible to @ref
-    string_t, for instance `std::string`.
-
-    @complexity Linear in the size of the passed @a val.
-
-    @throw std::bad_alloc if allocation for string value fails
-
-    @liveexample{The following code shows the construction of a string value
-    from a compatible type.,basic_json__CompatibleStringType}
-
-    @sa @ref basic_json(const string_t&) -- create a string value
-    @sa @ref basic_json(const typename string_t::value_type*) -- create a
-    string value from a character pointer
-
-    @since version 1.0.0
-    */
-    template<class CompatibleStringType, typename std::enable_if<
-                 std::is_constructible<string_t, CompatibleStringType>::value, int>::type = 0>
-    basic_json(const CompatibleStringType& val)
-        : basic_json(string_t(val))
-    {
-        assert_invariant();
-    }
-
-    /*!
-    @brief create a boolean (explicit)
-
-    Creates a JSON boolean type from a given value.
-
-    @param[in] val  a boolean value to store
-
-    @complexity Constant.
-
-    @liveexample{The example below demonstrates boolean
-    values.,basic_json__boolean_t}
-
-    @since version 1.0.0
-    */
-    basic_json(boolean_t val) noexcept
-        : m_type(value_t::boolean), m_value(val)
-    {
-        assert_invariant();
-    }
-
-    /*!
-    @brief create an integer number (explicit)
-
-    Create an integer number JSON value with a given content.
-
-    @tparam T A helper type to remove this function via SFINAE in case @ref
-    number_integer_t is the same as `int`. In this case, this constructor
-    would have the same signature as @ref basic_json(const int value). Note
-    the helper type @a T is not visible in this constructor's interface.
-
-    @param[in] val  an integer to create a JSON number from
-
-    @complexity Constant.
-
-    @liveexample{The example below shows the construction of an integer
-    number value.,basic_json__number_integer_t}
-
-    @sa @ref basic_json(const int) -- create a number value (integer)
-    @sa @ref basic_json(const CompatibleNumberIntegerType) -- create a number
-    value (integer) from a compatible number type
-
-    @since version 1.0.0
-    */
-    template<typename T, typename std::enable_if<
-                 not (std::is_same<T, int>::value) and
-                 std::is_same<T, number_integer_t>::value, int>::type = 0>
-    basic_json(const number_integer_t val) noexcept
-        : m_type(value_t::number_integer), m_value(val)
-    {
-        assert_invariant();
-    }
-
-    /*!
-    @brief create an integer number from an enum type (explicit)
-
-    Create an integer number JSON value with a given content.
-
-    @param[in] val  an integer to create a JSON number from
-
-    @note This constructor allows to pass enums directly to a constructor. As
-    C++ has no way of specifying the type of an anonymous enum explicitly, we
-    can only rely on the fact that such values implicitly convert to int. As
-    int may already be the same type of number_integer_t, we may need to
-    switch off the constructor @ref basic_json(const number_integer_t).
-
-    @complexity Constant.
-
-    @liveexample{The example below shows the construction of an integer
-    number value from an anonymous enum.,basic_json__const_int}
-
-    @sa @ref basic_json(const number_integer_t) -- create a number value
-    (integer)
-    @sa @ref basic_json(const CompatibleNumberIntegerType) -- create a number
-    value (integer) from a compatible number type
-
-    @since version 1.0.0
-    */
-    basic_json(const int val) noexcept
-        : m_type(value_t::number_integer),
-          m_value(static_cast<number_integer_t>(val))
-    {
-        assert_invariant();
-    }
-
-    /*!
-    @brief create an integer number (implicit)
-
-    Create an integer number JSON value with a given content. This constructor
-    allows any type @a CompatibleNumberIntegerType that can be used to
-    construct values of type @ref number_integer_t.
-
-    @tparam CompatibleNumberIntegerType An integer type which is compatible to
-    @ref number_integer_t. Examples include the types `int`, `int32_t`,
-    `long`, and `short`.
-
-    @param[in] val  an integer to create a JSON number from
-
-    @complexity Constant.
-
-    @liveexample{The example below shows the construction of several integer
-    number values from compatible
-    types.,basic_json__CompatibleIntegerNumberType}
-
-    @sa @ref basic_json(const number_integer_t) -- create a number value
-    (integer)
-    @sa @ref basic_json(const int) -- create a number value (integer)
-
-    @since version 1.0.0
-    */
-    template<typename CompatibleNumberIntegerType, typename std::enable_if<
-                 std::is_constructible<number_integer_t, CompatibleNumberIntegerType>::value and
-                 std::numeric_limits<CompatibleNumberIntegerType>::is_integer and
-                 std::numeric_limits<CompatibleNumberIntegerType>::is_signed,
-                 CompatibleNumberIntegerType>::type = 0>
-    basic_json(const CompatibleNumberIntegerType val) noexcept
-        : m_type(value_t::number_integer),
-          m_value(static_cast<number_integer_t>(val))
-    {
-        assert_invariant();
-    }
-
-    /*!
-    @brief create an unsigned integer number (explicit)
-
-    Create an unsigned integer number JSON value with a given content.
-
-    @tparam T  helper type to compare number_unsigned_t and unsigned int (not
-    visible in) the interface.
-
-    @param[in] val  an integer to create a JSON number from
-
-    @complexity Constant.
-
-    @sa @ref basic_json(const CompatibleNumberUnsignedType) -- create a number
-    value (unsigned integer) from a compatible number type
-
-    @since version 2.0.0
-    */
-    template<typename T, typename std::enable_if<
-                 not (std::is_same<T, int>::value) and
-                 std::is_same<T, number_unsigned_t>::value, int>::type = 0>
-    basic_json(const number_unsigned_t val) noexcept
-        : m_type(value_t::number_unsigned), m_value(val)
-    {
-        assert_invariant();
-    }
-
-    /*!
-    @brief create an unsigned number (implicit)
-
-    Create an unsigned number JSON value with a given content. This
-    constructor allows any type @a CompatibleNumberUnsignedType that can be
-    used to construct values of type @ref number_unsigned_t.
-
-    @tparam CompatibleNumberUnsignedType An integer type which is compatible
-    to @ref number_unsigned_t. Examples may include the types `unsigned int`,
-    `uint32_t`, or `unsigned short`.
-
-    @param[in] val  an unsigned integer to create a JSON number from
-
-    @complexity Constant.
-
-    @sa @ref basic_json(const number_unsigned_t) -- create a number value
-    (unsigned)
-
-    @since version 2.0.0
-    */
-    template<typename CompatibleNumberUnsignedType, typename std::enable_if <
-                 std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and
-                 std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and
-                 not std::numeric_limits<CompatibleNumberUnsignedType>::is_signed,
-                 CompatibleNumberUnsignedType>::type = 0>
-    basic_json(const CompatibleNumberUnsignedType val) noexcept
-        : m_type(value_t::number_unsigned),
-          m_value(static_cast<number_unsigned_t>(val))
-    {
-        assert_invariant();
-    }
-
-    /*!
-    @brief create a floating-point number (explicit)
-
-    Create a floating-point number JSON value with a given content.
-
-    @param[in] val  a floating-point value to create a JSON number from
-
-    @note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6
-    disallows NaN values:
-    > Numeric values that cannot be represented in the grammar below (such as
-    > Infinity and NaN) are not permitted.
-    In case the parameter @a val is not a number, a JSON null value is created
-    instead.
-
-    @complexity Constant.
-
-    @liveexample{The following example creates several floating-point
-    values.,basic_json__number_float_t}
-
-    @sa @ref basic_json(const CompatibleNumberFloatType) -- create a number
-    value (floating-point) from a compatible number type
-
-    @since version 1.0.0
-    */
-    basic_json(const number_float_t val) noexcept
-        : m_type(value_t::number_float), m_value(val)
-    {
-        // replace infinity and NAN by null
-        if (not std::isfinite(val))
-        {
-            m_type = value_t::null;
-            m_value = json_value();
-        }
-
-        assert_invariant();
-    }
-
-    /*!
-    @brief create an floating-point number (implicit)
-
-    Create an floating-point number JSON value with a given content. This
-    constructor allows any type @a CompatibleNumberFloatType that can be used
-    to construct values of type @ref number_float_t.
-
-    @tparam CompatibleNumberFloatType A floating-point type which is
-    compatible to @ref number_float_t. Examples may include the types `float`
-    or `double`.
-
-    @param[in] val  a floating-point to create a JSON number from
-
-    @note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6
-    disallows NaN values:
-    > Numeric values that cannot be represented in the grammar below (such as
-    > Infinity and NaN) are not permitted.
-    In case the parameter @a val is not a number, a JSON null value is
-    created instead.
-
-    @complexity Constant.
-
-    @liveexample{The example below shows the construction of several
-    floating-point number values from compatible
-    types.,basic_json__CompatibleNumberFloatType}
-
-    @sa @ref basic_json(const number_float_t) -- create a number value
-    (floating-point)
-
-    @since version 1.0.0
-    */
-    template<typename CompatibleNumberFloatType, typename = typename std::enable_if<
-                 std::is_constructible<number_float_t, CompatibleNumberFloatType>::value and
-                 std::is_floating_point<CompatibleNumberFloatType>::value>::type>
-    basic_json(const CompatibleNumberFloatType val) noexcept
-        : basic_json(number_float_t(val))
-    {
+        JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));
         assert_invariant();
     }
 
@@ -1654,7 +2102,7 @@
             // if object is wanted but impossible, throw an exception
             if (manual_type == value_t::object and not is_an_object)
             {
-                throw std::domain_error("cannot create object from initializer list");
+                JSON_THROW(std::domain_error("cannot create object from initializer list"));
             }
         }
 
@@ -1832,7 +2280,7 @@
         // make sure iterator fits the current value
         if (first.m_object != last.m_object)
         {
-            throw std::domain_error("iterators are not compatible");
+            JSON_THROW(std::domain_error("iterators are not compatible"));
         }
 
         // copy type from first iterator
@@ -1849,7 +2297,7 @@
             {
                 if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end())
                 {
-                    throw std::out_of_range("iterators out of range");
+                    JSON_THROW(std::out_of_range("iterators out of range"));
                 }
                 break;
             }
@@ -1894,19 +2342,21 @@
 
             case value_t::object:
             {
-                m_value.object = create<object_t>(first.m_it.object_iterator, last.m_it.object_iterator);
+                m_value.object = create<object_t>(first.m_it.object_iterator,
+                                                  last.m_it.object_iterator);
                 break;
             }
 
             case value_t::array:
             {
-                m_value.array = create<array_t>(first.m_it.array_iterator, last.m_it.array_iterator);
+                m_value.array = create<array_t>(first.m_it.array_iterator,
+                                                last.m_it.array_iterator);
                 break;
             }
 
             default:
             {
-                throw std::domain_error("cannot use construct with iterators from " + first.m_object->type_name());
+                JSON_THROW(std::domain_error("cannot use construct with iterators from " + first.m_object->type_name()));
             }
         }
 
@@ -2579,244 +3029,99 @@
     // value access //
     //////////////////
 
-    /// get an object (explicit)
-    template<class T, typename std::enable_if<
-                 std::is_convertible<typename object_t::key_type, typename T::key_type>::value and
-                 std::is_convertible<basic_json_t, typename T::mapped_type>::value, int>::type = 0>
-    T get_impl(T*) const
-    {
-        if (is_object())
-        {
-            return T(m_value.object->begin(), m_value.object->end());
-        }
-        else
-        {
-            throw std::domain_error("type must be object, but is " + type_name());
-        }
-    }
-
-    /// get an object (explicit)
-    object_t get_impl(object_t*) const
-    {
-        if (is_object())
-        {
-            return *(m_value.object);
-        }
-        else
-        {
-            throw std::domain_error("type must be object, but is " + type_name());
-        }
-    }
-
-    /// get an array (explicit)
-    template<class T, typename std::enable_if<
-                 std::is_convertible<basic_json_t, typename T::value_type>::value and
-                 not std::is_same<basic_json_t, typename T::value_type>::value and
-                 not std::is_arithmetic<T>::value and
-                 not std::is_convertible<std::string, T>::value and
-                 not has_mapped_type<T>::value, int>::type = 0>
-    T get_impl(T*) const
-    {
-        if (is_array())
-        {
-            T to_vector;
-            std::transform(m_value.array->begin(), m_value.array->end(),
-                           std::inserter(to_vector, to_vector.end()), [](basic_json i)
-            {
-                return i.get<typename T::value_type>();
-            });
-            return to_vector;
-        }
-        else
-        {
-            throw std::domain_error("type must be array, but is " + type_name());
-        }
-    }
-
-    /// get an array (explicit)
-    template<class T, typename std::enable_if<
-                 std::is_convertible<basic_json_t, T>::value and
-                 not std::is_same<basic_json_t, T>::value, int>::type = 0>
-    std::vector<T> get_impl(std::vector<T>*) const
-    {
-        if (is_array())
-        {
-            std::vector<T> to_vector;
-            to_vector.reserve(m_value.array->size());
-            std::transform(m_value.array->begin(), m_value.array->end(),
-                           std::inserter(to_vector, to_vector.end()), [](basic_json i)
-            {
-                return i.get<T>();
-            });
-            return to_vector;
-        }
-        else
-        {
-            throw std::domain_error("type must be array, but is " + type_name());
-        }
-    }
-
-    /// get an array (explicit)
-    template<class T, typename std::enable_if<
-                 std::is_same<basic_json, typename T::value_type>::value and
-                 not has_mapped_type<T>::value, int>::type = 0>
-    T get_impl(T*) const
-    {
-        if (is_array())
-        {
-            return T(m_value.array->begin(), m_value.array->end());
-        }
-        else
-        {
-            throw std::domain_error("type must be array, but is " + type_name());
-        }
-    }
-
-    /// get an array (explicit)
-    array_t get_impl(array_t*) const
-    {
-        if (is_array())
-        {
-            return *(m_value.array);
-        }
-        else
-        {
-            throw std::domain_error("type must be array, but is " + type_name());
-        }
-    }
-
-    /// get a string (explicit)
-    template<typename T, typename std::enable_if<
-                 std::is_convertible<string_t, T>::value, int>::type = 0>
-    T get_impl(T*) const
-    {
-        if (is_string())
-        {
-            return *m_value.string;
-        }
-        else
-        {
-            throw std::domain_error("type must be string, but is " + type_name());
-        }
-    }
-
-    /// get a number (explicit)
-    template<typename T, typename std::enable_if<
-                 std::is_arithmetic<T>::value, int>::type = 0>
-    T get_impl(T*) const
-    {
-        switch (m_type)
-        {
-            case value_t::number_integer:
-            {
-                return static_cast<T>(m_value.number_integer);
-            }
-
-            case value_t::number_unsigned:
-            {
-                return static_cast<T>(m_value.number_unsigned);
-            }
-
-            case value_t::number_float:
-            {
-                return static_cast<T>(m_value.number_float);
-            }
-
-            default:
-            {
-                throw std::domain_error("type must be number, but is " + type_name());
-            }
-        }
-    }
-
     /// get a boolean (explicit)
-    constexpr boolean_t get_impl(boolean_t*) const
+    boolean_t get_impl(boolean_t* /*unused*/) const
     {
-        return is_boolean()
-               ? m_value.boolean
-               : throw std::domain_error("type must be boolean, but is " + type_name());
+        if (is_boolean())
+        {
+            return m_value.boolean;
+        }
+        else
+        {
+            JSON_THROW(std::domain_error("type must be boolean, but is " + type_name()));
+        }
     }
 
     /// get a pointer to the value (object)
-    object_t* get_impl_ptr(object_t*) noexcept
+    object_t* get_impl_ptr(object_t* /*unused*/) noexcept
     {
         return is_object() ? m_value.object : nullptr;
     }
 
     /// get a pointer to the value (object)
-    constexpr const object_t* get_impl_ptr(const object_t*) const noexcept
+    constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept
     {
         return is_object() ? m_value.object : nullptr;
     }
 
     /// get a pointer to the value (array)
-    array_t* get_impl_ptr(array_t*) noexcept
+    array_t* get_impl_ptr(array_t* /*unused*/) noexcept
     {
         return is_array() ? m_value.array : nullptr;
     }
 
     /// get a pointer to the value (array)
-    constexpr const array_t* get_impl_ptr(const array_t*) const noexcept
+    constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept
     {
         return is_array() ? m_value.array : nullptr;
     }
 
     /// get a pointer to the value (string)
-    string_t* get_impl_ptr(string_t*) noexcept
+    string_t* get_impl_ptr(string_t* /*unused*/) noexcept
     {
         return is_string() ? m_value.string : nullptr;
     }
 
     /// get a pointer to the value (string)
-    constexpr const string_t* get_impl_ptr(const string_t*) const noexcept
+    constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept
     {
         return is_string() ? m_value.string : nullptr;
     }
 
     /// get a pointer to the value (boolean)
-    boolean_t* get_impl_ptr(boolean_t*) noexcept
+    boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept
     {
         return is_boolean() ? &m_value.boolean : nullptr;
     }
 
     /// get a pointer to the value (boolean)
-    constexpr const boolean_t* get_impl_ptr(const boolean_t*) const noexcept
+    constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept
     {
         return is_boolean() ? &m_value.boolean : nullptr;
     }
 
     /// get a pointer to the value (integer number)
-    number_integer_t* get_impl_ptr(number_integer_t*) noexcept
+    number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept
     {
         return is_number_integer() ? &m_value.number_integer : nullptr;
     }
 
     /// get a pointer to the value (integer number)
-    constexpr const number_integer_t* get_impl_ptr(const number_integer_t*) const noexcept
+    constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept
     {
         return is_number_integer() ? &m_value.number_integer : nullptr;
     }
 
     /// get a pointer to the value (unsigned number)
-    number_unsigned_t* get_impl_ptr(number_unsigned_t*) noexcept
+    number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept
     {
         return is_number_unsigned() ? &m_value.number_unsigned : nullptr;
     }
 
     /// get a pointer to the value (unsigned number)
-    constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t*) const noexcept
+    constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept
     {
         return is_number_unsigned() ? &m_value.number_unsigned : nullptr;
     }
 
     /// get a pointer to the value (floating-point number)
-    number_float_t* get_impl_ptr(number_float_t*) noexcept
+    number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept
     {
         return is_number_float() ? &m_value.number_float : nullptr;
     }
 
     /// get a pointer to the value (floating-point number)
-    constexpr const number_float_t* get_impl_ptr(const number_float_t*) const noexcept
+    constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept
     {
         return is_number_float() ? &m_value.number_float : nullptr;
     }
@@ -2845,34 +3150,69 @@
         {
             return *ptr;
         }
-        else
-        {
-            throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " +
-                                    obj.type_name());
-        }
+
+        throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " +
+                                obj.type_name());
     }
 
   public:
-
     /// @name value access
     /// Direct access to the stored value of a JSON value.
     /// @{
 
     /*!
+    @brief get special-case overload
+
+    This overloads avoids a lot of template boilerplate, it can be seen as the
+    identity method
+
+    @tparam BasicJsonType == @ref basic_json
+
+    @return a copy of *this
+
+    @complexity Constant.
+
+    @since version 2.1.0
+    */
+    template <
+        typename BasicJsonType,
+        detail::enable_if_t<std::is_same<typename std::remove_const<BasicJsonType>::type,
+                                         basic_json_t>::value,
+                            int> = 0 >
+    basic_json get() const
+    {
+        return *this;
+    }
+
+    /*!
     @brief get a value (explicit)
 
-    Explicit type conversion between the JSON value and a compatible value.
+    Explicit type conversion between the JSON value and a compatible value
+    which is [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible)
+    and [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible).
+    The value is converted by calling the @ref json_serializer<ValueType>
+    `from_json()` method.
 
-    @tparam ValueType non-pointer type compatible to the JSON value, for
-    instance `int` for JSON integer numbers, `bool` for JSON booleans, or
-    `std::vector` types for JSON arrays
+    The function is equivalent to executing
+    @code {.cpp}
+    ValueType ret;
+    JSONSerializer<ValueType>::from_json(*this, ret);
+    return ret;
+    @endcode
 
-    @return copy of the JSON value, converted to type @a ValueType
+    This overloads is chosen if:
+    - @a ValueType is not @ref basic_json,
+    - @ref json_serializer<ValueType> has a `from_json()` method of the form
+      `void from_json(const @ref basic_json&, ValueType&)`, and
+    - @ref json_serializer<ValueType> does not have a `from_json()` method of
+      the form `ValueType from_json(const @ref basic_json&)`
 
-    @throw std::domain_error in case passed type @a ValueType is incompatible
-    to JSON; example: `"type must be object, but is null"`
+    @tparam ValueTypeCV the provided value type
+    @tparam ValueType the returned value type
 
-    @complexity Linear in the size of the JSON value.
+    @return copy of the JSON value, converted to @a ValueType
+
+    @throw what @ref json_serializer<ValueType> `from_json()` method throws
 
     @liveexample{The example below shows several conversions from JSON values
     to other types. There a few things to note: (1) Floating-point numbers can
@@ -2881,21 +3221,75 @@
     associative containers such as `std::unordered_map<std::string\,
     json>`.,get__ValueType_const}
 
-    @internal
-    The idea of using a casted null pointer to choose the correct
-    implementation is from <http://stackoverflow.com/a/8315197/266378>.
-    @endinternal
-
-    @sa @ref operator ValueType() const for implicit conversion
-    @sa @ref get() for pointer-member access
-
-    @since version 1.0.0
+    @since version 2.1.0
     */
-    template<typename ValueType, typename std::enable_if<
-                 not std::is_pointer<ValueType>::value, int>::type = 0>
-    ValueType get() const
+    template <
+        typename ValueTypeCV,
+        typename ValueType = detail::uncvref_t<ValueTypeCV>,
+        detail::enable_if_t <
+            not std::is_same<basic_json_t, ValueType>::value and
+            detail::has_from_json<basic_json_t, ValueType>::value and
+            not detail::has_non_default_from_json<basic_json_t, ValueType>::value,
+            int > = 0 >
+    ValueType get() const noexcept(noexcept(
+                                       JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>())))
     {
-        return get_impl(static_cast<ValueType*>(nullptr));
+        // we cannot static_assert on ValueTypeCV being non-const, because
+        // there is support for get<const basic_json_t>(), which is why we
+        // still need the uncvref
+        static_assert(not std::is_reference<ValueTypeCV>::value,
+                      "get() cannot be used with reference types, you might want to use get_ref()");
+        static_assert(std::is_default_constructible<ValueType>::value,
+                      "types must be DefaultConstructible when used with get()");
+
+        ValueType ret;
+        JSONSerializer<ValueType>::from_json(*this, ret);
+        return ret;
+    }
+
+    /*!
+    @brief get a value (explicit); special case
+
+    Explicit type conversion between the JSON value and a compatible value
+    which is **not** [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible)
+    and **not** [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible).
+    The value is converted by calling the @ref json_serializer<ValueType>
+    `from_json()` method.
+
+    The function is equivalent to executing
+    @code {.cpp}
+    return JSONSerializer<ValueTypeCV>::from_json(*this);
+    @endcode
+
+    This overloads is chosen if:
+    - @a ValueType is not @ref basic_json and
+    - @ref json_serializer<ValueType> has a `from_json()` method of the form
+      `ValueType from_json(const @ref basic_json&)`
+
+    @note If @ref json_serializer<ValueType> has both overloads of
+    `from_json()`, this one is chosen.
+
+    @tparam ValueTypeCV the provided value type
+    @tparam ValueType the returned value type
+
+    @return copy of the JSON value, converted to @a ValueType
+
+    @throw what @ref json_serializer<ValueType> `from_json()` method throws
+
+    @since version 2.1.0
+    */
+    template <
+        typename ValueTypeCV,
+        typename ValueType = detail::uncvref_t<ValueTypeCV>,
+        detail::enable_if_t<not std::is_same<basic_json_t, ValueType>::value and
+                            detail::has_non_default_from_json<basic_json_t,
+                                    ValueType>::value, int> = 0 >
+    ValueType get() const noexcept(noexcept(
+                                       JSONSerializer<ValueTypeCV>::from_json(std::declval<const basic_json_t&>())))
+    {
+        static_assert(not std::is_reference<ValueTypeCV>::value,
+                      "get() cannot be used with reference types, you might want to use get_ref()");
+        return JSONSerializer<ValueTypeCV>::from_json(*this);
     }
 
     /*!
@@ -3100,7 +3494,7 @@
     template < typename ValueType, typename std::enable_if <
                    not std::is_pointer<ValueType>::value and
                    not std::is_same<ValueType, typename string_t::value_type>::value
-#ifndef _MSC_VER  // Fix for issue #167 operator<< abiguity under VS2015
+#ifndef _MSC_VER  // fix for issue #167 operator<< abiguity under VS2015
                    and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value
 #endif
                    , int >::type = 0 >
@@ -3148,19 +3542,19 @@
         // at only works for arrays
         if (is_array())
         {
-            try
+            JSON_TRY
             {
                 return m_value.array->at(idx);
             }
-            catch (std::out_of_range&)
+            JSON_CATCH (std::out_of_range&)
             {
                 // create better exception explanation
-                throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+                JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range"));
             }
         }
         else
         {
-            throw std::domain_error("cannot use at() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use at() with " + type_name()));
         }
     }
 
@@ -3191,19 +3585,19 @@
         // at only works for arrays
         if (is_array())
         {
-            try
+            JSON_TRY
             {
                 return m_value.array->at(idx);
             }
-            catch (std::out_of_range&)
+            JSON_CATCH (std::out_of_range&)
             {
                 // create better exception explanation
-                throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+                JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range"));
             }
         }
         else
         {
-            throw std::domain_error("cannot use at() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use at() with " + type_name()));
         }
     }
 
@@ -3238,19 +3632,19 @@
         // at only works for objects
         if (is_object())
         {
-            try
+            JSON_TRY
             {
                 return m_value.object->at(key);
             }
-            catch (std::out_of_range&)
+            JSON_CATCH (std::out_of_range&)
             {
                 // create better exception explanation
-                throw std::out_of_range("key '" + key + "' not found");
+                JSON_THROW(std::out_of_range("key '" + key + "' not found"));
             }
         }
         else
         {
-            throw std::domain_error("cannot use at() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use at() with " + type_name()));
         }
     }
 
@@ -3285,19 +3679,19 @@
         // at only works for objects
         if (is_object())
         {
-            try
+            JSON_TRY
             {
                 return m_value.object->at(key);
             }
-            catch (std::out_of_range&)
+            JSON_CATCH (std::out_of_range&)
             {
                 // create better exception explanation
-                throw std::out_of_range("key '" + key + "' not found");
+                JSON_THROW(std::out_of_range("key '" + key + "' not found"));
             }
         }
         else
         {
-            throw std::domain_error("cannot use at() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use at() with " + type_name()));
         }
     }
 
@@ -3349,10 +3743,8 @@
 
             return m_value.array->operator[](idx);
         }
-        else
-        {
-            throw std::domain_error("cannot use operator[] with " + type_name());
-        }
+
+        JSON_THROW(std::domain_error("cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -3381,10 +3773,8 @@
         {
             return m_value.array->operator[](idx);
         }
-        else
-        {
-            throw std::domain_error("cannot use operator[] with " + type_name());
-        }
+
+        JSON_THROW(std::domain_error("cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -3429,10 +3819,8 @@
         {
             return m_value.object->operator[](key);
         }
-        else
-        {
-            throw std::domain_error("cannot use operator[] with " + type_name());
-        }
+
+        JSON_THROW(std::domain_error("cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -3473,10 +3861,8 @@
             assert(m_value.object->find(key) != m_value.object->end());
             return m_value.object->find(key)->second;
         }
-        else
-        {
-            throw std::domain_error("cannot use operator[] with " + type_name());
-        }
+
+        JSON_THROW(std::domain_error("cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -3590,10 +3976,8 @@
         {
             return m_value.object->operator[](key);
         }
-        else
-        {
-            throw std::domain_error("cannot use operator[] with " + type_name());
-        }
+
+        JSON_THROW(std::domain_error("cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -3635,10 +4019,8 @@
             assert(m_value.object->find(key) != m_value.object->end());
             return m_value.object->find(key)->second;
         }
-        else
-        {
-            throw std::domain_error("cannot use operator[] with " + type_name());
-        }
+
+        JSON_THROW(std::domain_error("cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -3702,14 +4084,12 @@
             {
                 return *it;
             }
-            else
-            {
-                return default_value;
-            }
+
+            return default_value;
         }
         else
         {
-            throw std::domain_error("cannot use value() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use value() with " + type_name()));
         }
     }
 
@@ -3771,19 +4151,17 @@
         if (is_object())
         {
             // if pointer resolves a value, return it or use default value
-            try
+            JSON_TRY
             {
                 return ptr.get_checked(this);
             }
-            catch (std::out_of_range&)
+            JSON_CATCH (std::out_of_range&)
             {
                 return default_value;
             }
         }
-        else
-        {
-            throw std::domain_error("cannot use value() with " + type_name());
-        }
+
+        JSON_THROW(std::domain_error("cannot use value() with " + type_name()));
     }
 
     /*!
@@ -3934,7 +4312,7 @@
         // make sure iterator fits the current value
         if (this != pos.m_object)
         {
-            throw std::domain_error("iterator does not fit current value");
+            JSON_THROW(std::domain_error("iterator does not fit current value"));
         }
 
         IteratorType result = end();
@@ -3949,7 +4327,7 @@
             {
                 if (not pos.m_it.primitive_iterator.is_begin())
                 {
-                    throw std::out_of_range("iterator out of range");
+                    JSON_THROW(std::out_of_range("iterator out of range"));
                 }
 
                 if (is_string())
@@ -3979,7 +4357,7 @@
 
             default:
             {
-                throw std::domain_error("cannot use erase() with " + type_name());
+                JSON_THROW(std::domain_error("cannot use erase() with " + type_name()));
             }
         }
 
@@ -4041,7 +4419,7 @@
         // make sure iterator fits the current value
         if (this != first.m_object or this != last.m_object)
         {
-            throw std::domain_error("iterators do not fit current value");
+            JSON_THROW(std::domain_error("iterators do not fit current value"));
         }
 
         IteratorType result = end();
@@ -4056,7 +4434,7 @@
             {
                 if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end())
                 {
-                    throw std::out_of_range("iterators out of range");
+                    JSON_THROW(std::out_of_range("iterators out of range"));
                 }
 
                 if (is_string())
@@ -4088,7 +4466,7 @@
 
             default:
             {
-                throw std::domain_error("cannot use erase() with " + type_name());
+                JSON_THROW(std::domain_error("cannot use erase() with " + type_name()));
             }
         }
 
@@ -4131,10 +4509,8 @@
         {
             return m_value.object->erase(key);
         }
-        else
-        {
-            throw std::domain_error("cannot use erase() with " + type_name());
-        }
+
+        JSON_THROW(std::domain_error("cannot use erase() with " + type_name()));
     }
 
     /*!
@@ -4168,14 +4544,14 @@
         {
             if (idx >= size())
             {
-                throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+                JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range"));
             }
 
             m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));
         }
         else
         {
-            throw std::domain_error("cannot use erase() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use erase() with " + type_name()));
         }
     }
 
@@ -4893,7 +5269,7 @@
         // push_back only works for null objects or arrays
         if (not(is_null() or is_array()))
         {
-            throw std::domain_error("cannot use push_back() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use push_back() with " + type_name()));
         }
 
         // transform null object into an array
@@ -4929,7 +5305,7 @@
         // push_back only works for null objects or arrays
         if (not(is_null() or is_array()))
         {
-            throw std::domain_error("cannot use push_back() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use push_back() with " + type_name()));
         }
 
         // transform null object into an array
@@ -4979,7 +5355,7 @@
         // push_back only works for null objects or objects
         if (not(is_null() or is_object()))
         {
-            throw std::domain_error("cannot use push_back() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use push_back() with " + type_name()));
         }
 
         // transform null object into an object
@@ -5079,7 +5455,7 @@
         // emplace_back only works for null objects or arrays
         if (not(is_null() or is_array()))
         {
-            throw std::domain_error("cannot use emplace_back() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use emplace_back() with " + type_name()));
         }
 
         // transform null object into an array
@@ -5097,8 +5473,8 @@
     /*!
     @brief add an object to an object if key does not exist
 
-    Inserts a new element into a JSON object constructed in-place with the given
-    @a args if there is no element with the key in the container. If the
+    Inserts a new element into a JSON object constructed in-place with the
+    given @a args if there is no element with the key in the container. If the
     function is called on a JSON null value, an empty object is created before
     appending the value created from @a args.
 
@@ -5127,7 +5503,7 @@
         // emplace only works for null objects or arrays
         if (not(is_null() or is_object()))
         {
-            throw std::domain_error("cannot use emplace() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use emplace() with " + type_name()));
         }
 
         // transform null object into an object
@@ -5163,8 +5539,8 @@
     @throw std::domain_error if @a pos is not an iterator of *this; example:
     `"iterator does not fit current value"`
 
-    @complexity Constant plus linear in the distance between pos and end of the
-    container.
+    @complexity Constant plus linear in the distance between pos and end of
+    the container.
 
     @liveexample{The example shows how `insert()` is used.,insert}
 
@@ -5178,7 +5554,7 @@
             // check if iterator pos fits to this JSON value
             if (pos.m_object != this)
             {
-                throw std::domain_error("iterator does not fit current value");
+                JSON_THROW(std::domain_error("iterator does not fit current value"));
             }
 
             // insert to array and return iterator
@@ -5186,10 +5562,8 @@
             result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val);
             return result;
         }
-        else
-        {
-            throw std::domain_error("cannot use insert() with " + type_name());
-        }
+
+        JSON_THROW(std::domain_error("cannot use insert() with " + type_name()));
     }
 
     /*!
@@ -5233,7 +5607,7 @@
             // check if iterator pos fits to this JSON value
             if (pos.m_object != this)
             {
-                throw std::domain_error("iterator does not fit current value");
+                JSON_THROW(std::domain_error("iterator does not fit current value"));
             }
 
             // insert to array and return iterator
@@ -5241,10 +5615,8 @@
             result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);
             return result;
         }
-        else
-        {
-            throw std::domain_error("cannot use insert() with " + type_name());
-        }
+
+        JSON_THROW(std::domain_error("cannot use insert() with " + type_name()));
     }
 
     /*!
@@ -5282,24 +5654,24 @@
         // insert only works for arrays
         if (not is_array())
         {
-            throw std::domain_error("cannot use insert() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use insert() with " + type_name()));
         }
 
         // check if iterator pos fits to this JSON value
         if (pos.m_object != this)
         {
-            throw std::domain_error("iterator does not fit current value");
+            JSON_THROW(std::domain_error("iterator does not fit current value"));
         }
 
         // check if range iterators belong to the same JSON object
         if (first.m_object != last.m_object)
         {
-            throw std::domain_error("iterators do not fit");
+            JSON_THROW(std::domain_error("iterators do not fit"));
         }
 
         if (first.m_object == this or last.m_object == this)
         {
-            throw std::domain_error("passed iterators may not belong to container");
+            JSON_THROW(std::domain_error("passed iterators may not belong to container"));
         }
 
         // insert to array and return iterator
@@ -5340,13 +5712,13 @@
         // insert only works for arrays
         if (not is_array())
         {
-            throw std::domain_error("cannot use insert() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use insert() with " + type_name()));
         }
 
         // check if iterator pos fits to this JSON value
         if (pos.m_object != this)
         {
-            throw std::domain_error("iterator does not fit current value");
+            JSON_THROW(std::domain_error("iterator does not fit current value"));
         }
 
         // insert to array and return iterator
@@ -5394,8 +5766,8 @@
 
     @param[in,out] other array to exchange the contents with
 
-    @throw std::domain_error when JSON value is not an array; example: `"cannot
-    use swap() with string"`
+    @throw std::domain_error when JSON value is not an array; example:
+    `"cannot use swap() with string"`
 
     @complexity Constant.
 
@@ -5413,7 +5785,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use swap() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use swap() with " + type_name()));
         }
     }
 
@@ -5446,7 +5818,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use swap() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use swap() with " + type_name()));
         }
     }
 
@@ -5479,13 +5851,13 @@
         }
         else
         {
-            throw std::domain_error("cannot use swap() with " + type_name());
+            JSON_THROW(std::domain_error("cannot use swap() with " + type_name()));
         }
     }
 
     /// @}
 
-
+  public:
     //////////////////////////////////////////
     // lexicographical comparison operators //
     //////////////////////////////////////////
@@ -5493,40 +5865,6 @@
     /// @name lexicographical comparison operators
     /// @{
 
-  private:
-    /*!
-    @brief comparison operator for JSON types
-
-    Returns an ordering that is similar to Python:
-    - order: null < boolean < number < object < array < string
-    - furthermore, each type is not smaller than itself
-
-    @since version 1.0.0
-    */
-    friend bool operator<(const value_t lhs, const value_t rhs) noexcept
-    {
-        static constexpr std::array<uint8_t, 8> order = {{
-                0, // null
-                3, // object
-                4, // array
-                5, // string
-                1, // boolean
-                2, // integer
-                2, // unsigned
-                2, // float
-            }
-        };
-
-        // discarded values are not comparable
-        if (lhs == value_t::discarded or rhs == value_t::discarded)
-        {
-            return false;
-        }
-
-        return order[static_cast<std::size_t>(lhs)] < order[static_cast<std::size_t>(rhs)];
-    }
-
-  public:
     /*!
     @brief comparison: equal
 
@@ -6122,7 +6460,7 @@
     {
         // assertion to check that the iterator range is indeed contiguous,
         // see http://stackoverflow.com/a/35008842/266378 for more discussion
-        assert(std::accumulate(first, last, std::make_pair<bool, int>(true, 0),
+        assert(std::accumulate(first, last, std::pair<bool, int>(true, 0),
                                [&first](std::pair<bool, int> res, decltype(*first) val)
         {
             res.first &= (val == *(std::next(std::addressof(*first), res.second++)));
@@ -6323,11 +6661,11 @@
     {
         if (current_index + sizeof(T) + 1 > vec.size())
         {
-            throw std::out_of_range("cannot read " + std::to_string(sizeof(T)) + " bytes from vector");
+            JSON_THROW(std::out_of_range("cannot read " + std::to_string(sizeof(T)) + " bytes from vector"));
         }
 
         T result;
-        uint8_t* ptr = reinterpret_cast<uint8_t*>(&result);
+        auto* ptr = reinterpret_cast<uint8_t*>(&result);
         for (size_t i = 0; i < sizeof(T); ++i)
         {
             *ptr++ = vec[current_index + sizeof(T) - i];
@@ -6368,8 +6706,9 @@
                 if (j.m_value.number_integer >= 0)
                 {
                     // MessagePack does not differentiate between positive
-                    // signed integers and unsigned integers. Therefore, we used
-                    // the code from the value_t::number_unsigned case here.
+                    // signed integers and unsigned integers. Therefore, we
+                    // used the code from the value_t::number_unsigned case
+                    // here.
                     if (j.m_value.number_unsigned < 128)
                     {
                         // positive fixnum
@@ -6473,7 +6812,7 @@
             {
                 // float 64
                 v.push_back(0xcb);
-                const uint8_t* helper = reinterpret_cast<const uint8_t*>(&(j.m_value.number_float));
+                const auto* helper = reinterpret_cast<const uint8_t*>(&(j.m_value.number_float));
                 for (size_t i = 0; i < 8; ++i)
                 {
                     v.push_back(helper[7 - i]);
@@ -6644,8 +6983,8 @@
                 }
                 else
                 {
-                    // The conversions below encode the sign in the first byte,
-                    // and the value is converted to a positive number.
+                    // The conversions below encode the sign in the first
+                    // byte, and the value is converted to a positive number.
                     const auto positive_number = -1 - j.m_value.number_integer;
                     if (j.m_value.number_integer >= -24)
                     {
@@ -6716,7 +7055,7 @@
             {
                 // Double-Precision Float
                 v.push_back(0xfb);
-                const uint8_t* helper = reinterpret_cast<const uint8_t*>(&(j.m_value.number_float));
+                const auto* helper = reinterpret_cast<const uint8_t*>(&(j.m_value.number_float));
                 for (size_t i = 0; i < 8; ++i)
                 {
                     v.push_back(helper[7 - i]);
@@ -6850,12 +7189,12 @@
 
     To secure the access to the byte vector during CBOR/MessagePack
     deserialization, bytes are copied from the vector into buffers. This
-    function checks if the number of bytes to copy (@a len) does not exceed the
-    size @s size of the vector. Additionally, an @a offset is given from where
-    to start reading the bytes.
+    function checks if the number of bytes to copy (@a len) does not exceed
+    the size @s size of the vector. Additionally, an @a offset is given from
+    where to start reading the bytes.
 
-    This function checks whether reading the bytes is safe; that is, offset is a
-    valid index in the vector, offset+len
+    This function checks whether reading the bytes is safe; that is, offset is
+    a valid index in the vector, offset+len
 
     @param[in] size    size of the byte vector
     @param[in] len     number of bytes to read
@@ -6872,19 +7211,19 @@
         // simple case: requested length is greater than the vector's length
         if (len > size or offset > size)
         {
-            throw std::out_of_range("len out of range");
+            JSON_THROW(std::out_of_range("len out of range"));
         }
 
         // second case: adding offset would result in overflow
         if ((size > (std::numeric_limits<size_t>::max() - offset)))
         {
-            throw std::out_of_range("len+offset out of range");
+            JSON_THROW(std::out_of_range("len+offset out of range"));
         }
 
         // last case: reading past the end of the vector
         if (len + offset > size)
         {
-            throw std::out_of_range("len+offset out of range");
+            JSON_THROW(std::out_of_range("len+offset out of range"));
         }
     }
 
@@ -6916,7 +7255,7 @@
             {
                 return v[current_idx];
             }
-            else if (v[current_idx] <= 0x8f) // fixmap
+            if (v[current_idx] <= 0x8f) // fixmap
             {
                 basic_json result = value_t::object;
                 const size_t len = v[current_idx] & 0x0f;
@@ -6972,11 +7311,10 @@
                 case 0xca: // float 32
                 {
                     // copy bytes in reverse order into the double variable
-                    check_length(v.size(), sizeof(float), 1);
                     float res;
                     for (size_t byte = 0; byte < sizeof(float); ++byte)
                     {
-                        reinterpret_cast<uint8_t*>(&res)[sizeof(float) - byte - 1] = v[current_idx + 1 + byte];
+                        reinterpret_cast<uint8_t*>(&res)[sizeof(float) - byte - 1] = v.at(current_idx + 1 + byte);
                     }
                     idx += sizeof(float); // skip content bytes
                     return res;
@@ -6985,11 +7323,10 @@
                 case 0xcb: // float 64
                 {
                     // copy bytes in reverse order into the double variable
-                    check_length(v.size(), sizeof(double), 1);
                     double res;
                     for (size_t byte = 0; byte < sizeof(double); ++byte)
                     {
-                        reinterpret_cast<uint8_t*>(&res)[sizeof(double) - byte - 1] = v[current_idx + 1 + byte];
+                        reinterpret_cast<uint8_t*>(&res)[sizeof(double) - byte - 1] = v.at(current_idx + 1 + byte);
                     }
                     idx += sizeof(double); // skip content bytes
                     return res;
@@ -7122,7 +7459,7 @@
 
                 default:
                 {
-                    throw std::invalid_argument("error parsing a msgpack @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast<int>(v[current_idx])));
+                    JSON_THROW(std::invalid_argument("error parsing a msgpack @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast<int>(v[current_idx]))));
                 }
             }
         }
@@ -7551,7 +7888,6 @@
 
             case 0xf9: // Half-Precision Float (two-byte IEEE 754)
             {
-                check_length(v.size(), 2, 1);
                 idx += 2; // skip two content bytes
 
                 // code from RFC 7049, Appendix D, Figure 3:
@@ -7561,7 +7897,7 @@
                 // include at least decoding support for them even without such
                 // support. An example of a small decoder for half-precision
                 // floating-point numbers in the C language is shown in Fig. 3.
-                const int half = (v[current_idx + 1] << 8) + v[current_idx + 2];
+                const int half = (v.at(current_idx + 1) << 8) + v.at(current_idx + 2);
                 const int exp = (half >> 10) & 0x1f;
                 const int mant = half & 0x3ff;
                 double val;
@@ -7577,17 +7913,16 @@
                 {
                     val = mant == 0 ? INFINITY : NAN;
                 }
-                return half & 0x8000 ? -val : val;
+                return (half & 0x8000) != 0 ? -val : val;
             }
 
             case 0xfa: // Single-Precision Float (four-byte IEEE 754)
             {
                 // copy bytes in reverse order into the float variable
-                check_length(v.size(), sizeof(float), 1);
                 float res;
                 for (size_t byte = 0; byte < sizeof(float); ++byte)
                 {
-                    reinterpret_cast<uint8_t*>(&res)[sizeof(float) - byte - 1] = v[current_idx + 1 + byte];
+                    reinterpret_cast<uint8_t*>(&res)[sizeof(float) - byte - 1] = v.at(current_idx + 1 + byte);
                 }
                 idx += sizeof(float); // skip content bytes
                 return res;
@@ -7595,12 +7930,11 @@
 
             case 0xfb: // Double-Precision Float (eight-byte IEEE 754)
             {
-                check_length(v.size(), sizeof(double), 1);
                 // copy bytes in reverse order into the double variable
                 double res;
                 for (size_t byte = 0; byte < sizeof(double); ++byte)
                 {
-                    reinterpret_cast<uint8_t*>(&res)[sizeof(double) - byte - 1] = v[current_idx + 1 + byte];
+                    reinterpret_cast<uint8_t*>(&res)[sizeof(double) - byte - 1] = v.at(current_idx + 1 + byte);
                 }
                 idx += sizeof(double); // skip content bytes
                 return res;
@@ -7608,7 +7942,7 @@
 
             default: // anything else (0xFF is handled inside the other types)
             {
-                throw std::invalid_argument("error parsing a CBOR @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast<int>(v[current_idx])));
+                JSON_THROW(std::invalid_argument("error parsing a CBOR @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast<int>(v[current_idx]))));
             }
         }
     }
@@ -7728,7 +8062,6 @@
 
     /// @}
 
-  private:
     ///////////////////////////
     // convenience functions //
     ///////////////////////////
@@ -7743,29 +8076,35 @@
 
     @complexity Constant.
 
-    @since version 1.0.0
+    @liveexample{The following code exemplifies `type_name()` for all JSON
+    types.,type_name}
+
+    @since version 1.0.0, public since 2.1.0
     */
     std::string type_name() const
     {
-        switch (m_type)
         {
-            case value_t::null:
-                return "null";
-            case value_t::object:
-                return "object";
-            case value_t::array:
-                return "array";
-            case value_t::string:
-                return "string";
-            case value_t::boolean:
-                return "boolean";
-            case value_t::discarded:
-                return "discarded";
-            default:
-                return "number";
+            switch (m_type)
+            {
+                case value_t::null:
+                    return "null";
+                case value_t::object:
+                    return "object";
+                case value_t::array:
+                    return "array";
+                case value_t::string:
+                    return "string";
+                case value_t::boolean:
+                    return "boolean";
+                case value_t::discarded:
+                    return "discarded";
+                default:
+                    return "number";
+            }
         }
     }
 
+  private:
     /*!
     @brief calculates the extra space to escape a JSON string
 
@@ -7800,10 +8139,8 @@
                         // from c (1 byte) to \uxxxx (6 bytes)
                         return res + 5;
                     }
-                    else
-                    {
-                        return res;
-                    }
+
+                    return res;
                 }
             }
         });
@@ -8115,6 +8452,11 @@
     class primitive_iterator_t
     {
       public:
+
+        difference_type get_value() const noexcept
+        {
+            return m_it;
+        }
         /// set iterator to a defined beginning
         void set_begin() noexcept
         {
@@ -8139,16 +8481,87 @@
             return (m_it == end_value);
         }
 
-        /// return reference to the value to change and compare
-        operator difference_type& () noexcept
+        friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
         {
-            return m_it;
+            return lhs.m_it == rhs.m_it;
         }
 
-        /// return value to compare
-        constexpr operator difference_type () const noexcept
+        friend constexpr bool operator!=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
         {
-            return m_it;
+            return !(lhs == rhs);
+        }
+
+        friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+        {
+            return lhs.m_it < rhs.m_it;
+        }
+
+        friend constexpr bool operator<=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+        {
+            return lhs.m_it <= rhs.m_it;
+        }
+
+        friend constexpr bool operator>(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+        {
+            return lhs.m_it > rhs.m_it;
+        }
+
+        friend constexpr bool operator>=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+        {
+            return lhs.m_it >= rhs.m_it;
+        }
+
+        primitive_iterator_t operator+(difference_type i)
+        {
+            auto result = *this;
+            result += i;
+            return result;
+        }
+
+        friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+        {
+            return lhs.m_it - rhs.m_it;
+        }
+
+        friend std::ostream& operator<<(std::ostream& os, primitive_iterator_t it)
+        {
+            return os << it.m_it;
+        }
+
+        primitive_iterator_t& operator++()
+        {
+            ++m_it;
+            return *this;
+        }
+
+        primitive_iterator_t& operator++(int)
+        {
+            m_it++;
+            return *this;
+        }
+
+        primitive_iterator_t& operator--()
+        {
+            --m_it;
+            return *this;
+        }
+
+        primitive_iterator_t& operator--(int)
+        {
+            m_it--;
+            return *this;
+        }
+
+        primitive_iterator_t& operator+=(difference_type n)
+        {
+            m_it += n;
+            return *this;
+        }
+
+        primitive_iterator_t& operator-=(difference_type n)
+        {
+            m_it -= n;
+            return *this;
         }
 
       private:
@@ -8500,7 +8913,7 @@
 
                 case basic_json::value_t::null:
                 {
-                    throw std::out_of_range("cannot get value");
+                    JSON_THROW(std::out_of_range("cannot get value"));
                 }
 
                 default:
@@ -8509,10 +8922,8 @@
                     {
                         return *m_object;
                     }
-                    else
-                    {
-                        throw std::out_of_range("cannot get value");
-                    }
+
+                    JSON_THROW(std::out_of_range("cannot get value"));
                 }
             }
         }
@@ -8545,10 +8956,8 @@
                     {
                         return m_object;
                     }
-                    else
-                    {
-                        throw std::out_of_range("cannot get value");
-                    }
+
+                    JSON_THROW(std::out_of_range("cannot get value"));
                 }
             }
         }
@@ -8648,7 +9057,7 @@
             // if objects are not the same, the comparison is undefined
             if (m_object != other.m_object)
             {
-                throw std::domain_error("cannot compare iterators of different containers");
+                JSON_THROW(std::domain_error("cannot compare iterators of different containers"));
             }
 
             assert(m_object != nullptr);
@@ -8690,7 +9099,7 @@
             // if objects are not the same, the comparison is undefined
             if (m_object != other.m_object)
             {
-                throw std::domain_error("cannot compare iterators of different containers");
+                JSON_THROW(std::domain_error("cannot compare iterators of different containers"));
             }
 
             assert(m_object != nullptr);
@@ -8699,7 +9108,7 @@
             {
                 case basic_json::value_t::object:
                 {
-                    throw std::domain_error("cannot compare order of object iterators");
+                    JSON_THROW(std::domain_error("cannot compare order of object iterators"));
                 }
 
                 case basic_json::value_t::array:
@@ -8753,7 +9162,7 @@
             {
                 case basic_json::value_t::object:
                 {
-                    throw std::domain_error("cannot use offsets with object iterators");
+                    JSON_THROW(std::domain_error("cannot use offsets with object iterators"));
                 }
 
                 case basic_json::value_t::array:
@@ -8815,7 +9224,7 @@
             {
                 case basic_json::value_t::object:
                 {
-                    throw std::domain_error("cannot use offsets with object iterators");
+                    JSON_THROW(std::domain_error("cannot use offsets with object iterators"));
                 }
 
                 case basic_json::value_t::array:
@@ -8842,7 +9251,7 @@
             {
                 case basic_json::value_t::object:
                 {
-                    throw std::domain_error("cannot use operator[] for object iterators");
+                    JSON_THROW(std::domain_error("cannot use operator[] for object iterators"));
                 }
 
                 case basic_json::value_t::array:
@@ -8852,19 +9261,17 @@
 
                 case basic_json::value_t::null:
                 {
-                    throw std::out_of_range("cannot get value");
+                    JSON_THROW(std::out_of_range("cannot get value"));
                 }
 
                 default:
                 {
-                    if (m_it.primitive_iterator == -n)
+                    if (m_it.primitive_iterator.get_value() == -n)
                     {
                         return *m_object;
                     }
-                    else
-                    {
-                        throw std::out_of_range("cannot get value");
-                    }
+
+                    JSON_THROW(std::out_of_range("cannot get value"));
                 }
             }
         }
@@ -8881,10 +9288,8 @@
             {
                 return m_it.object_iterator->first;
             }
-            else
-            {
-                throw std::domain_error("cannot use key() for non-object iterators");
-            }
+
+            JSON_THROW(std::domain_error("cannot use key() for non-object iterators"));
         }
 
         /*!
@@ -9069,7 +9474,7 @@
             // immediately abort if stream is erroneous
             if (s.fail())
             {
-                throw std::invalid_argument("stream error");
+                JSON_THROW(std::invalid_argument("stream error"));
             }
 
             // fill buffer
@@ -9136,7 +9541,7 @@
                 }
                 else
                 {
-                    throw std::invalid_argument("missing or wrong low surrogate");
+                    JSON_THROW(std::invalid_argument("missing or wrong low surrogate"));
                 }
             }
 
@@ -9170,7 +9575,7 @@
             }
             else
             {
-                throw std::out_of_range("code points above 0x10FFFF are invalid");
+                JSON_THROW(std::out_of_range("code points above 0x10FFFF are invalid"));
             }
 
             return result;
@@ -9359,7 +9764,7 @@
             assert(m_marker == nullptr or m_marker  <= m_limit);
 
             // number of processed characters (p)
-            const size_t num_processed_chars = static_cast<size_t>(m_start - m_content);
+            const auto num_processed_chars = static_cast<size_t>(m_start - m_content);
             // offset for m_marker wrt. to m_start
             const auto offset_marker = (m_marker == nullptr) ? 0 : m_marker - m_start;
             // number of unprocessed characters (u)
@@ -9552,7 +9957,7 @@
                                 // make sure there is a subsequent unicode
                                 if ((i + 6 >= m_limit) or * (i + 5) != '\\' or * (i + 6) != 'u')
                                 {
-                                    throw std::invalid_argument("missing low surrogate");
+                                    JSON_THROW(std::invalid_argument("missing low surrogate"));
                                 }
 
                                 // get code yyyy from uxxxx\uyyyy
@@ -9565,7 +9970,7 @@
                             else if (codepoint >= 0xDC00 and codepoint <= 0xDFFF)
                             {
                                 // we found a lone low surrogate
-                                throw std::invalid_argument("missing high surrogate");
+                                JSON_THROW(std::invalid_argument("missing high surrogate"));
                             }
                             else
                             {
@@ -9690,7 +10095,7 @@
             for (; curptr < m_cursor; curptr++)
             {
                 // quickly skip tests if a digit
-                if (*curptr < '0' || *curptr > '9')
+                if (*curptr < '0' or* curptr > '9')
                 {
                     if (*curptr == '.')
                     {
@@ -9751,7 +10156,7 @@
             else
             {
                 // parse with strtod
-                result.m_value.number_float = str_to_float_t(static_cast<number_float_t*>(nullptr), NULL);
+                result.m_value.number_float = str_to_float_t(static_cast<number_float_t*>(nullptr), nullptr);
 
                 // replace infinity and NAN by null
                 if (not std::isfinite(result.m_value.number_float))
@@ -10043,7 +10448,7 @@
                               "'") :
                               lexer::token_type_name(last_token));
                 error_msg += "; expected " + lexer::token_type_name(t);
-                throw std::invalid_argument(error_msg);
+                JSON_THROW(std::invalid_argument(error_msg));
             }
         }
 
@@ -10055,7 +10460,7 @@
                 error_msg += (last_token == lexer::token_type::parse_error ? ("'" +  m_lexer.get_token_string() +
                               "'") :
                               lexer::token_type_name(last_token));
-                throw std::invalid_argument(error_msg);
+                JSON_THROW(std::invalid_argument(error_msg));
             }
         }
 
@@ -10151,7 +10556,7 @@
         {
             if (is_root())
             {
-                throw std::domain_error("JSON pointer has no parent");
+                JSON_THROW(std::domain_error("JSON pointer has no parent"));
             }
 
             auto last = reference_tokens.back();
@@ -10169,7 +10574,7 @@
         {
             if (is_root())
             {
-                throw std::domain_error("JSON pointer has no parent");
+                JSON_THROW(std::domain_error("JSON pointer has no parent"));
             }
 
             json_pointer result = *this;
@@ -10230,7 +10635,7 @@
                     */
                     default:
                     {
-                        throw std::domain_error("invalid value to unflatten");
+                        JSON_THROW(std::domain_error("invalid value to unflatten"));
                     }
                 }
             }
@@ -10298,7 +10703,7 @@
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            throw std::domain_error("array index must not begin with '0'");
+                            JSON_THROW(std::domain_error("array index must not begin with '0'"));
                         }
 
                         if (reference_token == "-")
@@ -10316,7 +10721,7 @@
 
                     default:
                     {
-                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                        JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'"));
                     }
                 }
             }
@@ -10350,7 +10755,7 @@
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            throw std::domain_error("array index must not begin with '0'");
+                            JSON_THROW(std::domain_error("array index must not begin with '0'"));
                         }
 
                         // note: at performs range check
@@ -10360,7 +10765,7 @@
 
                     default:
                     {
-                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                        JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'"));
                     }
                 }
             }
@@ -10402,7 +10807,7 @@
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            throw std::domain_error("array index must not begin with '0'");
+                            JSON_THROW(std::domain_error("array index must not begin with '0'"));
                         }
 
                         // use unchecked array access
@@ -10412,7 +10817,7 @@
 
                     default:
                     {
-                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                        JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'"));
                     }
                 }
             }
@@ -10446,7 +10851,7 @@
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            throw std::domain_error("array index must not begin with '0'");
+                            JSON_THROW(std::domain_error("array index must not begin with '0'"));
                         }
 
                         // note: at performs range check
@@ -10456,7 +10861,7 @@
 
                     default:
                     {
-                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                        JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'"));
                     }
                 }
             }
@@ -10478,7 +10883,7 @@
             // check if nonempty reference string begins with slash
             if (reference_string[0] != '/')
             {
-                throw std::domain_error("JSON pointer must be empty or begin with '/'");
+                JSON_THROW(std::domain_error("JSON pointer must be empty or begin with '/'"));
             }
 
             // extract the reference tokens:
@@ -10486,7 +10891,7 @@
             // - start: position after the previous slash
             for (
                 // search for the first slash after the first character
-                size_t slash = reference_string.find_first_of("/", 1),
+                size_t slash = reference_string.find_first_of('/', 1),
                 // set the beginning of the first reference token
                 start = 1;
                 // we can stop if start == string::npos+1 = 0
@@ -10495,16 +10900,16 @@
                 // (will eventually be 0 if slash == std::string::npos)
                 start = slash + 1,
                 // find next slash
-                slash = reference_string.find_first_of("/", start))
+                slash = reference_string.find_first_of('/', start))
             {
                 // use the text between the beginning of the reference token
                 // (start) and the last slash (slash).
                 auto reference_token = reference_string.substr(start, slash - start);
 
                 // check reference tokens are properly escaped
-                for (size_t pos = reference_token.find_first_of("~");
+                for (size_t pos = reference_token.find_first_of('~');
                         pos != std::string::npos;
-                        pos = reference_token.find_first_of("~", pos + 1))
+                        pos = reference_token.find_first_of('~', pos + 1))
                 {
                     assert(reference_token[pos] == '~');
 
@@ -10513,7 +10918,7 @@
                             (reference_token[pos + 1] != '0' and
                              reference_token[pos + 1] != '1'))
                     {
-                        throw std::domain_error("escape error: '~' must be followed with '0' or '1'");
+                        JSON_THROW(std::domain_error("escape error: '~' must be followed with '0' or '1'"));
                     }
                 }
 
@@ -10639,7 +11044,7 @@
         {
             if (not value.is_object())
             {
-                throw std::domain_error("only objects can be unflattened");
+                JSON_THROW(std::domain_error("only objects can be unflattened"));
             }
 
             basic_json result;
@@ -10649,7 +11054,7 @@
             {
                 if (not element.second.is_primitive())
                 {
-                    throw std::domain_error("values in object must be primitive");
+                    JSON_THROW(std::domain_error("values in object must be primitive"));
                 }
 
                 // assign value to reference pointed to by JSON pointer; Note
@@ -10664,6 +11069,18 @@
         }
 
       private:
+        friend bool operator==(json_pointer const& lhs,
+                               json_pointer const& rhs) noexcept
+        {
+            return lhs.reference_tokens == rhs.reference_tokens;
+        }
+
+        friend bool operator!=(json_pointer const& lhs,
+                               json_pointer const& rhs) noexcept
+        {
+            return !(lhs == rhs);
+        }
+
         /// the reference tokens
         std::vector<std::string> reference_tokens {};
     };
@@ -10978,7 +11395,7 @@
                             if (static_cast<size_type>(idx) > parent.size())
                             {
                                 // avoid undefined behavior
-                                throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+                                JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range"));
                             }
                             else
                             {
@@ -11016,7 +11433,7 @@
                 }
                 else
                 {
-                    throw std::out_of_range("key '" + last_path + "' not found");
+                    JSON_THROW(std::out_of_range("key '" + last_path + "' not found"));
                 }
             }
             else if (parent.is_array())
@@ -11030,7 +11447,7 @@
         if (not json_patch.is_array())
         {
             // a JSON patch must be an array of objects
-            throw std::invalid_argument("JSON patch must be an array of objects");
+            JSON_THROW(std::invalid_argument("JSON patch must be an array of objects"));
         }
 
         // iterate and apply th eoperations
@@ -11050,13 +11467,13 @@
                 // check if desired value is present
                 if (it == val.m_value.object->end())
                 {
-                    throw std::invalid_argument(error_msg + " must have member '" + member + "'");
+                    JSON_THROW(std::invalid_argument(error_msg + " must have member '" + member + "'"));
                 }
 
                 // check if result is of type string
                 if (string_type and not it->second.is_string())
                 {
-                    throw std::invalid_argument(error_msg + " must have string member '" + member + "'");
+                    JSON_THROW(std::invalid_argument(error_msg + " must have string member '" + member + "'"));
                 }
 
                 // no error: return value
@@ -11066,7 +11483,7 @@
             // type check
             if (not val.is_object())
             {
-                throw std::invalid_argument("JSON patch must be an array of objects");
+                JSON_THROW(std::invalid_argument("JSON patch must be an array of objects"));
             }
 
             // collect mandatory members
@@ -11125,13 +11542,13 @@
                 case patch_operations::test:
                 {
                     bool success = false;
-                    try
+                    JSON_TRY
                     {
                         // check if "value" matches the one at "path"
                         // the "path" location must exist - use at()
                         success = (result.at(ptr) == get_value("test", "value", false));
                     }
-                    catch (std::out_of_range&)
+                    JSON_CATCH (std::out_of_range&)
                     {
                         // ignore out of range errors: success remains false
                     }
@@ -11139,7 +11556,7 @@
                     // throw an exception if test fails
                     if (not success)
                     {
-                        throw std::domain_error("unsuccessful: " + val.dump());
+                        JSON_THROW(std::domain_error("unsuccessful: " + val.dump()));
                     }
 
                     break;
@@ -11149,7 +11566,7 @@
                 {
                     // op must be "add", "remove", "replace", "move", "copy", or
                     // "test"
-                    throw std::invalid_argument("operation value '" + op + "' is invalid");
+                    JSON_THROW(std::invalid_argument("operation value '" + op + "' is invalid"));
                 }
             }
         }
@@ -11324,7 +11741,6 @@
     /// @}
 };
 
-
 /////////////
 // presets //
 /////////////
@@ -11338,7 +11754,7 @@
 @since version 1.0.0
 */
 using json = basic_json<>;
-}
+} // namespace nlohmann
 
 
 ///////////////////////
@@ -11379,7 +11795,7 @@
         return h(j.dump());
     }
 };
-}
+} // namespace std
 
 /*!
 @brief user-defined string literal for JSON values
@@ -11422,4 +11838,10 @@
     #pragma GCC diagnostic pop
 #endif
 
+// clean up
+#undef JSON_THROW
+#undef JSON_TRY
+#undef JSON_CATCH
+#undef JSON_DEPRECATED
+
 #endif
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 47785ae..0ceb6bf 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -26,14 +26,17 @@
     "src/unit-iterators2.cpp"
     "src/unit-json_patch.cpp"
     "src/unit-json_pointer.cpp"
+    "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"
 )
 
diff --git a/test/Makefile b/test/Makefile
index 68520bd..0b235ba 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -30,6 +30,7 @@
           src/unit-iterators2.cpp \
           src/unit-json_patch.cpp \
           src/unit-json_pointer.cpp \
+          src/unit-meta.cpp \
           src/unit-modifiers.cpp \
           src/unit-msgpack.cpp \
           src/unit-pointer_access.cpp \
@@ -37,8 +38,8 @@
           src/unit-reference_access.cpp \
           src/unit-regression.cpp \
           src/unit-serialization.cpp \
-          src/unit-unicode.cpp \
-          src/unit-testsuites.cpp
+          src/unit-testsuites.cpp \
+          src/unit-unicode.cpp
 
 OBJECTS = $(SOURCES:.cpp=.o)
 
@@ -51,7 +52,7 @@
 all: $(TESTCASES)
 
 clean:
-	rm -fr json_unit $(OBJECTS) $(SOURCES:.cpp=.gcno) $(SOURCES:.cpp=.gcda) $(TESTCASES)
+	rm -fr json_unit $(OBJECTS) $(SOURCES:.cpp=.gcno) $(SOURCES:.cpp=.gcda) $(TESTCASES) parse_afl_fuzzer parse_cbor_fuzzer parse_msgpack_fuzzer
 
 ##############################################################################
 # single test file
@@ -74,7 +75,7 @@
 	@echo "[CXXLD] $@"
 	@$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) -DCATCH_CONFIG_MAIN $< -o $@
 
-TEST_PATTERN = "*"
+TEST_PATTERN ?= "*"
 TEST_PREFIX = ""
 check: $(TESTCASES)
 	@cd .. ; for testcase in $(TESTCASES); do echo "Executing $$testcase..."; $(TEST_PREFIX)test/$$testcase $(TEST_PATTERN) || exit 1; done
@@ -84,13 +85,14 @@
 # fuzzer
 ##############################################################################
 
+FUZZER_ENGINE = src/fuzzer-driver_afl.cpp
 fuzzers: parse_afl_fuzzer parse_cbor_fuzzer parse_msgpack_fuzzer
 
 parse_afl_fuzzer:
-	$(CXX) $(CXXFLAGS) $(CPPFLAGS) src/fuzzer-driver_afl.cpp src/fuzzer-parse_json.cpp -o $@
+	$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(FUZZER_ENGINE) src/fuzzer-parse_json.cpp -o $@
 
 parse_cbor_fuzzer:
-	$(CXX) $(CXXFLAGS) $(CPPFLAGS) src/fuzzer-driver_afl.cpp src/fuzzer-parse_cbor.cpp -o $@
+	$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(FUZZER_ENGINE) src/fuzzer-parse_cbor.cpp -o $@
 
 parse_msgpack_fuzzer:
-	$(CXX) $(CXXFLAGS) $(CPPFLAGS) src/fuzzer-driver_afl.cpp src/fuzzer-parse_msgpack.cpp -o $@
+	$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(FUZZER_ENGINE) src/fuzzer-parse_msgpack.cpp -o $@
diff --git a/test/data/big-list-of-naughty-strings/LICENSE b/test/data/big-list-of-naughty-strings/LICENSE
new file mode 100644
index 0000000..3a04164
--- /dev/null
+++ b/test/data/big-list-of-naughty-strings/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Max Woolf
+
+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.
+
diff --git a/test/data/big-list-of-naughty-strings/blns.json b/test/data/big-list-of-naughty-strings/blns.json
new file mode 100644
index 0000000..ed832d4
--- /dev/null
+++ b/test/data/big-list-of-naughty-strings/blns.json
@@ -0,0 +1,496 @@
+[
+  "", 
+  "undefined", 
+  "undef", 
+  "null", 
+  "NULL", 
+  "(null)", 
+  "nil", 
+  "NIL", 
+  "true", 
+  "false", 
+  "True", 
+  "False", 
+  "TRUE", 
+  "FALSE", 
+  "None", 
+  "hasOwnProperty", 
+  "\\", 
+  "\\\\", 
+  "0", 
+  "1", 
+  "1.00", 
+  "$1.00", 
+  "1/2", 
+  "1E2", 
+  "1E02", 
+  "1E+02", 
+  "-1", 
+  "-1.00", 
+  "-$1.00", 
+  "-1/2", 
+  "-1E2", 
+  "-1E02", 
+  "-1E+02", 
+  "1/0", 
+  "0/0", 
+  "-2147483648/-1", 
+  "-9223372036854775808/-1", 
+  "0.00", 
+  "0..0", 
+  ".", 
+  "0.0.0", 
+  "0,00", 
+  "0,,0", 
+  ",", 
+  "0,0,0", 
+  "0.0/0", 
+  "1.0/0.0", 
+  "0.0/0.0", 
+  "1,0/0,0", 
+  "0,0/0,0", 
+  "--1", 
+  "-", 
+  "-.", 
+  "-,", 
+  "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 
+  "NaN", 
+  "Infinity", 
+  "-Infinity", 
+  "INF", 
+  "1#INF", 
+  "-1#IND", 
+  "1#QNAN", 
+  "1#SNAN", 
+  "1#IND", 
+  "0x0", 
+  "0xffffffff", 
+  "0xffffffffffffffff", 
+  "0xabad1dea", 
+  "123456789012345678901234567890123456789", 
+  "1,000.00", 
+  "1 000.00", 
+  "1'000.00", 
+  "1,000,000.00", 
+  "1 000 000.00", 
+  "1'000'000.00", 
+  "1.000,00", 
+  "1 000,00", 
+  "1'000,00", 
+  "1.000.000,00", 
+  "1 000 000,00", 
+  "1'000'000,00", 
+  "01000", 
+  "08", 
+  "09", 
+  "2.2250738585072011e-308", 
+  ",./;'[]\\-=", 
+  "<>?:\"{}|_+", 
+  "!@#$%^&*()`~", 
+  "Ω≈ç√∫˜µ≤≥÷", 
+  "åß∂ƒ©˙∆˚¬…æ", 
+  "œ∑´®†¥¨ˆøπ“‘", 
+  "¡™£¢∞§¶•ªº–≠", 
+  "¸˛Ç◊ı˜Â¯˘¿", 
+  "ÅÍÎÏ˝ÓÔÒÚÆ☃", 
+  "Œ„´‰ˇÁ¨ˆØ∏”’", 
+  "`⁄€‹›fifl‡°·‚—±", 
+  "⅛⅜⅝⅞", 
+  "ЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюя", 
+  "٠١٢٣٤٥٦٧٨٩", 
+  "⁰⁴⁵", 
+  "₀₁₂", 
+  "⁰⁴⁵₀₁₂", 
+  "ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็ ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็ ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็", 
+  "'", 
+  "\"", 
+  "''", 
+  "\"\"", 
+  "'\"'", 
+  "\"''''\"'\"", 
+  "\"'\"'\"''''\"", 
+  "<foo val=“bar” />", 
+  "<foo val=“bar” />", 
+  "<foo val=”bar“ />", 
+  "<foo val=`bar' />", 
+  "田中さんにあげて下さい", 
+  "パーティーへ行かないか", 
+  "和製漢語", 
+  "部落格", 
+  "사회과학원 어학연구소", 
+  "찦차를 타고 온 펲시맨과 쑛다리 똠방각하", 
+  "社會科學院語學研究所", 
+  "울란바토르", 
+  "𠜎𠜱𠝹𠱓𠱸𠲖𠳏", 
+  "ヽ༼ຈل͜ຈ༽ノ ヽ༼ຈل͜ຈ༽ノ ", 
+  "(。◕ ∀ ◕。)", 
+  "`ィ(´∀`∩", 
+  "__ロ(,_,*)", 
+  "・( ̄∀ ̄)・:*:", 
+  "゚・✿ヾ╲(。◕‿◕。)╱✿・゚", 
+  ",。・:*:・゜’( ☻ ω ☻ )。・:*:・゜’", 
+  "(╯°□°)╯︵ ┻━┻)  ", 
+  "(ノಥ益ಥ)ノ ┻━┻", 
+  "┬─┬ノ( º _ ºノ)", 
+  "( ͡° ͜ʖ ͡°)", 
+  "😍", 
+  "👩🏽", 
+  "👾 🙇 💁 🙅 🙆 🙋 🙎 🙍 ", 
+  "🐵 🙈 🙉 🙊", 
+  "❤️ 💔 💌 💕 💞 💓 💗 💖 💘 💝 💟 💜 💛 💚 💙", 
+  "✋🏿 💪🏿 👐🏿 🙌🏿 👏🏿 🙏🏿", 
+  "🚾 🆒 🆓 🆕 🆖 🆗 🆙 🏧", 
+  "0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟", 
+  "🇺🇸🇷🇺🇸 🇦🇫🇦🇲🇸                                                                                          ", 
+  "🇺🇸🇷🇺🇸🇦🇫🇦🇲", 
+  "🇺🇸🇷🇺🇸🇦", 
+  "123", 
+  "١٢٣", 
+  "ثم نفس سقطت وبالتحديد،, جزيرتي باستخدام أن دنو. إذ هنا؟ الستار وتنصيب كان. أهّل ايطاليا، بريطانيا-فرنسا قد أخذ. سليمان، إتفاقية بين ما, يذكر الحدود أي بعد, معاملة بولندا، الإطلاق عل إيو.", 
+  "בְּרֵאשִׁית, בָּרָא אֱלֹהִים, אֵת הַשָּׁמַיִם, וְאֵת הָאָרֶץ", 
+  "הָיְתָהtestالصفحات التّحول", 
+  "﷽", 
+  "ﷺ", 
+  "مُنَاقَشَةُ سُبُلِ اِسْتِخْدَامِ اللُّغَةِ فِي النُّظُمِ الْقَائِمَةِ وَفِيم يَخُصَّ التَّطْبِيقَاتُ الْحاسُوبِيَّةُ، ", 
+  "​", 
+  " ", 
+  "᠎", 
+  " ", 
+  "", 
+  "␣", 
+  "␢", 
+  "␡", 
+  "‪‪test‪", 
+  "‫test‫", 
+  "
test
", 
+  "test⁠test‫", 
+  "⁦test⁧", 
+  "Ṱ̺̺̕o͞ ̷i̲̬͇̪͙n̝̗͕v̟̜̘̦͟o̶̙̰̠kè͚̮̺̪̹̱̤ ̖t̝͕̳̣̻̪͞h̼͓̲̦̳̘̲e͇̣̰̦̬͎ ̢̼̻̱̘h͚͎͙̜̣̲ͅi̦̲̣̰̤v̻͍e̺̭̳̪̰-m̢iͅn̖̺̞̲̯̰d̵̼̟͙̩̼̘̳ ̞̥̱̳̭r̛̗̘e͙p͠r̼̞̻̭̗e̺̠̣͟s̘͇̳͍̝͉e͉̥̯̞̲͚̬͜ǹ̬͎͎̟̖͇̤t͍̬̤͓̼̭͘ͅi̪̱n͠g̴͉ ͏͉ͅc̬̟h͡a̫̻̯͘o̫̟̖͍̙̝͉s̗̦̲.̨̹͈̣", 
+  "̡͓̞ͅI̗̘̦͝n͇͇͙v̮̫ok̲̫̙͈i̖͙̭̹̠̞n̡̻̮̣̺g̲͈͙̭͙̬͎ ̰t͔̦h̞̲e̢̤ ͍̬̲͖f̴̘͕̣è͖ẹ̥̩l͖͔͚i͓͚̦͠n͖͍̗͓̳̮g͍ ̨o͚̪͡f̘̣̬ ̖̘͖̟͙̮c҉͔̫͖͓͇͖ͅh̵̤̣͚͔á̗̼͕ͅo̼̣̥s̱͈̺̖̦̻͢.̛̖̞̠̫̰", 
+  "̗̺͖̹̯͓Ṯ̤͍̥͇͈h̲́e͏͓̼̗̙̼̣͔ ͇̜̱̠͓͍ͅN͕͠e̗̱z̘̝̜̺͙p̤̺̹͍̯͚e̠̻̠͜r̨̤͍̺̖͔̖̖d̠̟̭̬̝͟i̦͖̩͓͔̤a̠̗̬͉̙n͚͜ ̻̞̰͚ͅh̵͉i̳̞v̢͇ḙ͎͟-҉̭̩̼͔m̤̭̫i͕͇̝̦n̗͙ḍ̟ ̯̲͕͞ǫ̟̯̰̲͙̻̝f ̪̰̰̗̖̭̘͘c̦͍̲̞͍̩̙ḥ͚a̮͎̟̙͜ơ̩̹͎s̤.̝̝ ҉Z̡̖̜͖̰̣͉̜a͖̰͙̬͡l̲̫̳͍̩g̡̟̼̱͚̞̬ͅo̗͜.̟", 
+  "̦H̬̤̗̤͝e͜ ̜̥̝̻͍̟́w̕h̖̯͓o̝͙̖͎̱̮ ҉̺̙̞̟͈W̷̼̭a̺̪͍į͈͕̭͙̯̜t̶̼̮s̘͙͖̕ ̠̫̠B̻͍͙͉̳ͅe̵h̵̬͇̫͙i̹͓̳̳̮͎̫̕n͟d̴̪̜̖ ̰͉̩͇͙̲͞ͅT͖̼͓̪͢h͏͓̮̻e̬̝̟ͅ ̤̹̝W͙̞̝͔͇͝ͅa͏͓͔̹̼̣l̴͔̰̤̟͔ḽ̫.͕", 
+  "Z̮̞̠͙͔ͅḀ̗̞͈̻̗Ḷ͙͎̯̹̞͓G̻O̭̗̮", 
+  "˙ɐnbᴉlɐ ɐuƃɐɯ ǝɹolop ʇǝ ǝɹoqɐl ʇn ʇunpᴉpᴉɔuᴉ ɹodɯǝʇ poɯsnᴉǝ op pǝs 'ʇᴉlǝ ƃuᴉɔsᴉdᴉpɐ ɹnʇǝʇɔǝsuoɔ 'ʇǝɯɐ ʇᴉs ɹolop ɯnsdᴉ ɯǝɹo˥", 
+  "00˙Ɩ$-", 
+  "The quick brown fox jumps over the lazy dog", 
+  "𝐓𝐡𝐞 𝐪𝐮𝐢𝐜𝐤 𝐛𝐫𝐨𝐰𝐧 𝐟𝐨𝐱 𝐣𝐮𝐦𝐩𝐬 𝐨𝐯𝐞𝐫 𝐭𝐡𝐞 𝐥𝐚𝐳𝐲 𝐝𝐨𝐠", 
+  "𝕿𝖍𝖊 𝖖𝖚𝖎𝖈𝖐 𝖇𝖗𝖔𝖜𝖓 𝖋𝖔𝖝 𝖏𝖚𝖒𝖕𝖘 𝖔𝖛𝖊𝖗 𝖙𝖍𝖊 𝖑𝖆𝖟𝖞 𝖉𝖔𝖌", 
+  "𝑻𝒉𝒆 𝒒𝒖𝒊𝒄𝒌 𝒃𝒓𝒐𝒘𝒏 𝒇𝒐𝒙 𝒋𝒖𝒎𝒑𝒔 𝒐𝒗𝒆𝒓 𝒕𝒉𝒆 𝒍𝒂𝒛𝒚 𝒅𝒐𝒈", 
+  "𝓣𝓱𝓮 𝓺𝓾𝓲𝓬𝓴 𝓫𝓻𝓸𝔀𝓷 𝓯𝓸𝔁 𝓳𝓾𝓶𝓹𝓼 𝓸𝓿𝓮𝓻 𝓽𝓱𝓮 𝓵𝓪𝔃𝔂 𝓭𝓸𝓰", 
+  "𝕋𝕙𝕖 𝕢𝕦𝕚𝕔𝕜 𝕓𝕣𝕠𝕨𝕟 𝕗𝕠𝕩 𝕛𝕦𝕞𝕡𝕤 𝕠𝕧𝕖𝕣 𝕥𝕙𝕖 𝕝𝕒𝕫𝕪 𝕕𝕠𝕘", 
+  "𝚃𝚑𝚎 𝚚𝚞𝚒𝚌𝚔 𝚋𝚛𝚘𝚠𝚗 𝚏𝚘𝚡 𝚓𝚞𝚖𝚙𝚜 𝚘𝚟𝚎𝚛 𝚝𝚑𝚎 𝚕𝚊𝚣𝚢 𝚍𝚘𝚐", 
+  "⒯⒣⒠ ⒬⒰⒤⒞⒦ ⒝⒭⒪⒲⒩ ⒡⒪⒳ ⒥⒰⒨⒫⒮ ⒪⒱⒠⒭ ⒯⒣⒠ ⒧⒜⒵⒴ ⒟⒪⒢", 
+  "<script>alert(123)</script>", 
+  "&lt;script&gt;alert(&#39;123&#39;);&lt;/script&gt;", 
+  "<img src=x onerror=alert(123) />", 
+  "<svg><script>123<1>alert(123)</script> ", 
+  "\"><script>alert(123)</script>", 
+  "'><script>alert(123)</script>", 
+  "><script>alert(123)</script>", 
+  "</script><script>alert(123)</script>", 
+  "< / script >< script >alert(123)< / script >", 
+  " onfocus=JaVaSCript:alert(123) autofocus ", 
+  "\" onfocus=JaVaSCript:alert(123) autofocus ", 
+  "' onfocus=JaVaSCript:alert(123) autofocus ", 
+  "<script>alert(123)</script>", 
+  "<sc<script>ript>alert(123)</sc</script>ript>", 
+  "--><script>alert(123)</script>", 
+  "\";alert(123);t=\"", 
+  "';alert(123);t='", 
+  "JavaSCript:alert(123)", 
+  ";alert(123);", 
+  "src=JaVaSCript:prompt(132)", 
+  "\"><script>alert(123);</script x=\"", 
+  "'><script>alert(123);</script x='", 
+  "><script>alert(123);</script x=", 
+  "\" autofocus onkeyup=\"javascript:alert(123)", 
+  "' autofocus onkeyup='javascript:alert(123)", 
+  "<script\\x20type=\"text/javascript\">javascript:alert(1);</script>", 
+  "<script\\x3Etype=\"text/javascript\">javascript:alert(1);</script>", 
+  "<script\\x0Dtype=\"text/javascript\">javascript:alert(1);</script>", 
+  "<script\\x09type=\"text/javascript\">javascript:alert(1);</script>", 
+  "<script\\x0Ctype=\"text/javascript\">javascript:alert(1);</script>", 
+  "<script\\x2Ftype=\"text/javascript\">javascript:alert(1);</script>", 
+  "<script\\x0Atype=\"text/javascript\">javascript:alert(1);</script>", 
+  "'`\"><\\x3Cscript>javascript:alert(1)</script>        ", 
+  "'`\"><\\x00script>javascript:alert(1)</script>", 
+  "ABC<div style=\"x\\x3Aexpression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:expression\\x5C(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:expression\\x00(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:exp\\x00ression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:exp\\x5Cression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:\\x0Aexpression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:\\x09expression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:\\xE3\\x80\\x80expression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:\\xE2\\x80\\x84expression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:\\xC2\\xA0expression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:\\xE2\\x80\\x80expression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:\\xE2\\x80\\x8Aexpression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:\\x0Dexpression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:\\x0Cexpression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:\\xE2\\x80\\x87expression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:\\xEF\\xBB\\xBFexpression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:\\x20expression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:\\xE2\\x80\\x88expression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:\\x00expression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:\\xE2\\x80\\x8Bexpression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:\\xE2\\x80\\x86expression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:\\xE2\\x80\\x85expression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:\\xE2\\x80\\x82expression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:\\x0Bexpression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:\\xE2\\x80\\x81expression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:\\xE2\\x80\\x83expression(javascript:alert(1)\">DEF", 
+  "ABC<div style=\"x:\\xE2\\x80\\x89expression(javascript:alert(1)\">DEF", 
+  "<a href=\"\\x0Bjavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x0Fjavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\xC2\\xA0javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x05javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\xE1\\xA0\\x8Ejavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x18javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x11javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\xE2\\x80\\x88javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\xE2\\x80\\x89javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\xE2\\x80\\x80javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x17javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x03javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x0Ejavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x1Ajavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x00javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x10javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\xE2\\x80\\x82javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x20javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x13javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x09javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\xE2\\x80\\x8Ajavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x14javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x19javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\xE2\\x80\\xAFjavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x1Fjavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\xE2\\x80\\x81javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x1Djavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\xE2\\x80\\x87javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x07javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\xE1\\x9A\\x80javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\xE2\\x80\\x83javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x04javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x01javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x08javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\xE2\\x80\\x84javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\xE2\\x80\\x86javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\xE3\\x80\\x80javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x12javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x0Djavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x0Ajavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x0Cjavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x15javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\xE2\\x80\\xA8javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x16javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x02javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x1Bjavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x06javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\xE2\\x80\\xA9javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\xE2\\x80\\x85javascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x1Ejavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\xE2\\x81\\x9Fjavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"\\x1Cjavascript:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"javascript\\x00:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"javascript\\x3A:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"javascript\\x09:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"javascript\\x0D:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "<a href=\"javascript\\x0A:javascript:alert(1)\" id=\"fuzzelement1\">test</a>", 
+  "`\"'><img src=xxx:x \\x0Aonerror=javascript:alert(1)>", 
+  "`\"'><img src=xxx:x \\x22onerror=javascript:alert(1)>", 
+  "`\"'><img src=xxx:x \\x0Bonerror=javascript:alert(1)>", 
+  "`\"'><img src=xxx:x \\x0Donerror=javascript:alert(1)>", 
+  "`\"'><img src=xxx:x \\x2Fonerror=javascript:alert(1)>", 
+  "`\"'><img src=xxx:x \\x09onerror=javascript:alert(1)>", 
+  "`\"'><img src=xxx:x \\x0Conerror=javascript:alert(1)>", 
+  "`\"'><img src=xxx:x \\x00onerror=javascript:alert(1)>", 
+  "`\"'><img src=xxx:x \\x27onerror=javascript:alert(1)>", 
+  "`\"'><img src=xxx:x \\x20onerror=javascript:alert(1)>", 
+  "\"`'><script>\\x3Bjavascript:alert(1)</script>", 
+  "\"`'><script>\\x0Djavascript:alert(1)</script>", 
+  "\"`'><script>\\xEF\\xBB\\xBFjavascript:alert(1)</script>", 
+  "\"`'><script>\\xE2\\x80\\x81javascript:alert(1)</script>", 
+  "\"`'><script>\\xE2\\x80\\x84javascript:alert(1)</script>", 
+  "\"`'><script>\\xE3\\x80\\x80javascript:alert(1)</script>", 
+  "\"`'><script>\\x09javascript:alert(1)</script>", 
+  "\"`'><script>\\xE2\\x80\\x89javascript:alert(1)</script>", 
+  "\"`'><script>\\xE2\\x80\\x85javascript:alert(1)</script>", 
+  "\"`'><script>\\xE2\\x80\\x88javascript:alert(1)</script>", 
+  "\"`'><script>\\x00javascript:alert(1)</script>", 
+  "\"`'><script>\\xE2\\x80\\xA8javascript:alert(1)</script>", 
+  "\"`'><script>\\xE2\\x80\\x8Ajavascript:alert(1)</script>", 
+  "\"`'><script>\\xE1\\x9A\\x80javascript:alert(1)</script>", 
+  "\"`'><script>\\x0Cjavascript:alert(1)</script>", 
+  "\"`'><script>\\x2Bjavascript:alert(1)</script>", 
+  "\"`'><script>\\xF0\\x90\\x96\\x9Ajavascript:alert(1)</script>", 
+  "\"`'><script>-javascript:alert(1)</script>", 
+  "\"`'><script>\\x0Ajavascript:alert(1)</script>", 
+  "\"`'><script>\\xE2\\x80\\xAFjavascript:alert(1)</script>", 
+  "\"`'><script>\\x7Ejavascript:alert(1)</script>", 
+  "\"`'><script>\\xE2\\x80\\x87javascript:alert(1)</script>", 
+  "\"`'><script>\\xE2\\x81\\x9Fjavascript:alert(1)</script>", 
+  "\"`'><script>\\xE2\\x80\\xA9javascript:alert(1)</script>", 
+  "\"`'><script>\\xC2\\x85javascript:alert(1)</script>", 
+  "\"`'><script>\\xEF\\xBF\\xAEjavascript:alert(1)</script>", 
+  "\"`'><script>\\xE2\\x80\\x83javascript:alert(1)</script>", 
+  "\"`'><script>\\xE2\\x80\\x8Bjavascript:alert(1)</script>", 
+  "\"`'><script>\\xEF\\xBF\\xBEjavascript:alert(1)</script>", 
+  "\"`'><script>\\xE2\\x80\\x80javascript:alert(1)</script>", 
+  "\"`'><script>\\x21javascript:alert(1)</script>", 
+  "\"`'><script>\\xE2\\x80\\x82javascript:alert(1)</script>", 
+  "\"`'><script>\\xE2\\x80\\x86javascript:alert(1)</script>", 
+  "\"`'><script>\\xE1\\xA0\\x8Ejavascript:alert(1)</script>", 
+  "\"`'><script>\\x0Bjavascript:alert(1)</script>", 
+  "\"`'><script>\\x20javascript:alert(1)</script>", 
+  "\"`'><script>\\xC2\\xA0javascript:alert(1)</script>", 
+  "<img \\x00src=x onerror=\"alert(1)\">", 
+  "<img \\x47src=x onerror=\"javascript:alert(1)\">", 
+  "<img \\x11src=x onerror=\"javascript:alert(1)\">", 
+  "<img \\x12src=x onerror=\"javascript:alert(1)\">", 
+  "<img\\x47src=x onerror=\"javascript:alert(1)\">", 
+  "<img\\x10src=x onerror=\"javascript:alert(1)\">", 
+  "<img\\x13src=x onerror=\"javascript:alert(1)\">", 
+  "<img\\x32src=x onerror=\"javascript:alert(1)\">", 
+  "<img\\x47src=x onerror=\"javascript:alert(1)\">", 
+  "<img\\x11src=x onerror=\"javascript:alert(1)\">", 
+  "<img \\x47src=x onerror=\"javascript:alert(1)\">", 
+  "<img \\x34src=x onerror=\"javascript:alert(1)\">", 
+  "<img \\x39src=x onerror=\"javascript:alert(1)\">", 
+  "<img \\x00src=x onerror=\"javascript:alert(1)\">", 
+  "<img src\\x09=x onerror=\"javascript:alert(1)\">", 
+  "<img src\\x10=x onerror=\"javascript:alert(1)\">", 
+  "<img src\\x13=x onerror=\"javascript:alert(1)\">", 
+  "<img src\\x32=x onerror=\"javascript:alert(1)\">", 
+  "<img src\\x12=x onerror=\"javascript:alert(1)\">", 
+  "<img src\\x11=x onerror=\"javascript:alert(1)\">", 
+  "<img src\\x00=x onerror=\"javascript:alert(1)\">", 
+  "<img src\\x47=x onerror=\"javascript:alert(1)\">", 
+  "<img src=x\\x09onerror=\"javascript:alert(1)\">", 
+  "<img src=x\\x10onerror=\"javascript:alert(1)\">", 
+  "<img src=x\\x11onerror=\"javascript:alert(1)\">", 
+  "<img src=x\\x12onerror=\"javascript:alert(1)\">", 
+  "<img src=x\\x13onerror=\"javascript:alert(1)\">", 
+  "<img[a][b][c]src[d]=x[e]onerror=[f]\"alert(1)\">", 
+  "<img src=x onerror=\\x09\"javascript:alert(1)\">", 
+  "<img src=x onerror=\\x10\"javascript:alert(1)\">", 
+  "<img src=x onerror=\\x11\"javascript:alert(1)\">", 
+  "<img src=x onerror=\\x12\"javascript:alert(1)\">", 
+  "<img src=x onerror=\\x32\"javascript:alert(1)\">", 
+  "<img src=x onerror=\\x00\"javascript:alert(1)\">", 
+  "<a href=java&#1&#2&#3&#4&#5&#6&#7&#8&#11&#12script:javascript:alert(1)>XXX</a>", 
+  "<img src=\"x` `<script>javascript:alert(1)</script>\"` `>", 
+  "<img src onerror /\" '\"= alt=javascript:alert(1)//\">", 
+  "<title onpropertychange=javascript:alert(1)></title><title title=>", 
+  "<a href=http://foo.bar/#x=`y></a><img alt=\"`><img src=x:x onerror=javascript:alert(1)></a>\">", 
+  "<!--[if]><script>javascript:alert(1)</script -->", 
+  "<!--[if<img src=x onerror=javascript:alert(1)//]> -->", 
+  "<script src=\"/\\%(jscript)s\"></script>", 
+  "<script src=\"\\\\%(jscript)s\"></script>", 
+  "<IMG \"\"\"><SCRIPT>alert(\"XSS\")</SCRIPT>\">", 
+  "<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>", 
+  "<IMG SRC=# onmouseover=\"alert('xxs')\">", 
+  "<IMG SRC= onmouseover=\"alert('xxs')\">", 
+  "<IMG onmouseover=\"alert('xxs')\">", 
+  "<IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>", 
+  "<IMG SRC=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>", 
+  "<IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>", 
+  "<IMG SRC=\"jav   ascript:alert('XSS');\">", 
+  "<IMG SRC=\"jav&#x09;ascript:alert('XSS');\">", 
+  "<IMG SRC=\"jav&#x0A;ascript:alert('XSS');\">", 
+  "<IMG SRC=\"jav&#x0D;ascript:alert('XSS');\">", 
+  "perl -e 'print \"<IMG SRC=java\\0script:alert(\\\"XSS\\\")>\";' > out", 
+  "<IMG SRC=\" &#14;  javascript:alert('XSS');\">", 
+  "<SCRIPT/XSS SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>", 
+  "<BODY onload!#$%&()*~+-_.,:;?@[/|\\]^`=alert(\"XSS\")>", 
+  "<SCRIPT/SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>", 
+  "<<SCRIPT>alert(\"XSS\");//<</SCRIPT>", 
+  "<SCRIPT SRC=http://ha.ckers.org/xss.js?< B >", 
+  "<SCRIPT SRC=//ha.ckers.org/.j>", 
+  "<IMG SRC=\"javascript:alert('XSS')\"", 
+  "<iframe src=http://ha.ckers.org/scriptlet.html <", 
+  "\\\";alert('XSS');//", 
+  "<u oncopy=alert()> Copy me</u>", 
+  "<i onwheel=alert(1)> Scroll over me </i>", 
+  "<plaintext>", 
+  "http://a/%%30%30", 
+  "</textarea><script>alert(123)</script>",
+  "1;DROP TABLE users", 
+  "1'; DROP TABLE users-- 1", 
+  "' OR 1=1 -- 1", 
+  "' OR '1'='1", 
+  " ", 
+  "%", 
+  "_", 
+  "-", 
+  "--", 
+  "--version", 
+  "--help", 
+  "$USER", 
+  "/dev/null; touch /tmp/blns.fail ; echo", 
+  "`touch /tmp/blns.fail`", 
+  "$(touch /tmp/blns.fail)", 
+  "@{[system \"touch /tmp/blns.fail\"]}", 
+  "eval(\"puts 'hello world'\")", 
+  "System(\"ls -al /\")", 
+  "`ls -al /`", 
+  "Kernel.exec(\"ls -al /\")", 
+  "Kernel.exit(1)", 
+  "%x('ls -al /')", 
+  "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><!DOCTYPE foo [ <!ELEMENT foo ANY ><!ENTITY xxe SYSTEM \"file:///etc/passwd\" >]><foo>&xxe;</foo>", 
+  "$HOME", 
+  "$ENV{'HOME'}", 
+  "%d", 
+  "%s", 
+  "{0}", 
+  "%*.*s", 
+  "../../../../../../../../../../../etc/passwd%00", 
+  "../../../../../../../../../../../etc/hosts", 
+  "() { 0; }; touch /tmp/blns.shellshock1.fail;", 
+  "() { _; } >_[$($())] { touch /tmp/blns.shellshock2.fail; }", 
+  "+++ATH0",
+  "<<< %s(un='%s') = %u", 
+  "CON", 
+  "PRN", 
+  "AUX", 
+  "CLOCK$", 
+  "NUL", 
+  "A:", 
+  "ZZ:", 
+  "COM1", 
+  "LPT1", 
+  "LPT2", 
+  "LPT3", 
+  "COM2", 
+  "COM3", 
+  "COM4", 
+  "DCC SEND STARTKEYLOGGER 0 0 0", 
+  "Scunthorpe General Hospital", 
+  "Penistone Community Church", 
+  "Lightwater Country Park", 
+  "Jimmy Clitheroe", 
+  "Horniman Museum", 
+  "shitake mushrooms", 
+  "RomansInSussex.co.uk", 
+  "http://www.cum.qc.ca/", 
+  "Craig Cockburn, Software Specialist", 
+  "Linda Callahan", 
+  "Dr. Herman I. Libshitz", 
+  "magna cum laude", 
+  "Super Bowl XXX", 
+  "medieval erection of parapets", 
+  "evaluate", 
+  "mocha", 
+  "expression", 
+  "Arsenal canal", 
+  "classic", 
+  "Tyson Gay", 
+  "Dick Van Dyke", 
+  "basement", 
+  "If you're reading this, you've been in a coma for almost 20 years now. We're trying a new technique. We don't know where this message will end up in your dream, but we hope it works. Please wake up, we miss you.", 
+  "Roses are \u001b[0;31mred\u001b[0m, violets are \u001b[0;34mblue. Hope you enjoy terminal hue", 
+  "But now...\u001b[20Cfor my greatest trick...\u001b[8m", 
+  "The quic\b\b\b\b\b\bk brown fo\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007x... [Beeeep]", 
+  "Powerلُلُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ冗"
+]
diff --git a/test/src/fuzzer-driver_afl.cpp b/test/src/fuzzer-driver_afl.cpp
index 0c173b4..b1699f3 100644
--- a/test/src/fuzzer-driver_afl.cpp
+++ b/test/src/fuzzer-driver_afl.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (fuzz test support)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 This file implements a driver for American Fuzzy Lop (afl-fuzz). It relies on
diff --git a/test/src/fuzzer-parse_cbor.cpp b/test/src/fuzzer-parse_cbor.cpp
index bba5674..1b6664f 100644
--- a/test/src/fuzzer-parse_cbor.cpp
+++ b/test/src/fuzzer-parse_cbor.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (fuzz test support)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 This file implements a parser test suitable for fuzz testing. Given a byte
diff --git a/test/src/fuzzer-parse_json.cpp b/test/src/fuzzer-parse_json.cpp
index f61df56..40ccf58 100644
--- a/test/src/fuzzer-parse_json.cpp
+++ b/test/src/fuzzer-parse_json.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (fuzz test support)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 This file implements a parser test suitable for fuzz testing. Given a byte
diff --git a/test/src/fuzzer-parse_msgpack.cpp b/test/src/fuzzer-parse_msgpack.cpp
index 0355db3..381dc75 100644
--- a/test/src/fuzzer-parse_msgpack.cpp
+++ b/test/src/fuzzer-parse_msgpack.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (fuzz test support)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 This file implements a parser test suitable for fuzz testing. Given a byte
diff --git a/test/src/unit-algorithms.cpp b/test/src/unit-algorithms.cpp
index 0905d05..3a73c10 100644
--- a/test/src/unit-algorithms.cpp
+++ b/test/src/unit-algorithms.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-allocator.cpp b/test/src/unit-allocator.cpp
index f11d853..ae45ac2 100644
--- a/test/src/unit-allocator.cpp
+++ b/test/src/unit-allocator.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -80,7 +80,7 @@
         }
         else
         {
-            ::new(reinterpret_cast<void*>(p)) T(std::forward<Args>(args)...);
+            ::new (reinterpret_cast<void*>(p)) T(std::forward<Args>(args)...);
         }
     }
 
diff --git a/test/src/unit-capacity.cpp b/test/src/unit-capacity.cpp
index d3ee33d..44a35b9 100644
--- a/test/src/unit-capacity.cpp
+++ b/test/src/unit-capacity.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp
index 92238b7..539d408 100644
--- a/test/src/unit-cbor.cpp
+++ b/test/src/unit-cbor.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -1187,7 +1187,7 @@
     }
 }
 
-TEST_CASE("CBOR regressions")
+TEST_CASE("CBOR regressions", "[!throws]")
 {
     SECTION("fuzz test results")
     {
diff --git a/test/src/unit-class_const_iterator.cpp b/test/src/unit-class_const_iterator.cpp
index 13ce7c3..1a05bec 100644
--- a/test/src/unit-class_const_iterator.cpp
+++ b/test/src/unit-class_const_iterator.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -91,7 +91,7 @@
                 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 @@
                 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 @@
                 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 @@
                 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 @@
                 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 @@
                 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 @@
             {
                 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 @@
             {
                 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 @@
             {
                 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 @@
             {
                 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()));
             }
         }
     }
diff --git a/test/src/unit-class_iterator.cpp b/test/src/unit-class_iterator.cpp
index 640bc81..e3ef6a0 100644
--- a/test/src/unit-class_iterator.cpp
+++ b/test/src/unit-class_iterator.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -75,7 +75,7 @@
                 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 @@
                 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 @@
                 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 @@
                 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 @@
                 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 @@
                 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 @@
             {
                 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 @@
             {
                 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 @@
             {
                 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 @@
             {
                 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()));
             }
         }
     }
diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp
index 33ea610..ac43de8 100644
--- a/test/src/unit-class_lexer.cpp
+++ b/test/src/unit-class_lexer.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -38,86 +38,86 @@
     {
         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 @@
                 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 @@
                 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;
                 }
             }
diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp
index 0951504..3dfad5b 100644
--- a/test/src/unit-class_parser.cpp
+++ b/test/src/unit-class_parser.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-comparison.cpp b/test/src/unit-comparison.cpp
index 7f89729..f1e1aeb 100644
--- a/test/src/unit-comparison.cpp
+++ b/test/src/unit-comparison.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-concepts.cpp b/test/src/unit-concepts.cpp
index 1c04b62..d65f740 100644
--- a/test/src/unit-concepts.cpp
+++ b/test/src/unit-concepts.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-constructor1.cpp b/test/src/unit-constructor1.cpp
index 6bfb440..93546c5 100644
--- a/test/src/unit-constructor1.cpp
+++ b/test/src/unit-constructor1.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -912,7 +912,7 @@
 
             SECTION("array")
             {
-                json j { {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} , 13 };
+                json j { {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 };
                 CHECK(j.type() == json::value_t::array);
             }
         }
diff --git a/test/src/unit-constructor2.cpp b/test/src/unit-constructor2.cpp
index ab1e43c..95cb87d 100644
--- a/test/src/unit-constructor2.cpp
+++ b/test/src/unit-constructor2.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-convenience.cpp b/test/src/unit-convenience.cpp
index cd78f83..a9355da 100644
--- a/test/src/unit-convenience.cpp
+++ b/test/src/unit-convenience.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp
index b82127b..fca807a 100644
--- a/test/src/unit-conversions.cpp
+++ b/test/src/unit-conversions.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -160,12 +160,30 @@
         {
             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 @@
             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 @@
                 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");
diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp
index 21e3bb4..aaf4627 100644
--- a/test/src/unit-deserialization.cpp
+++ b/test/src/unit-deserialization.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-element_access1.cpp b/test/src/unit-element_access1.cpp
index a596ac2..5f264ac 100644
--- a/test/src/unit-element_access1.cpp
+++ b/test/src/unit-element_access1.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-element_access2.cpp b/test/src/unit-element_access2.cpp
index 1ba6aa6..353d66d 100644
--- a/test/src/unit-element_access2.cpp
+++ b/test/src/unit-element_access2.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -298,25 +298,6 @@
                     CHECK(j_const.value("/array"_json_pointer, json({10, 100})) == json({1, 2, 3}));
                 }
 
-                SECTION("access non-existing value")
-                {
-                    CHECK(j.value("/not/existing"_json_pointer, 2) == 2);
-                    CHECK(j.value("/not/existing"_json_pointer, 2u) == 2u);
-                    CHECK(j.value("/not/existing"_json_pointer, false) == false);
-                    CHECK(j.value("/not/existing"_json_pointer, "bar") == "bar");
-                    CHECK(j.value("/not/existing"_json_pointer, 12.34) == Approx(12.34));
-                    CHECK(j.value("/not/existing"_json_pointer, json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
-                    CHECK(j.value("/not/existing"_json_pointer, json({10, 100})) == json({10, 100}));
-
-                    CHECK(j_const.value("/not/existing"_json_pointer, 2) == 2);
-                    CHECK(j_const.value("/not/existing"_json_pointer, 2u) == 2u);
-                    CHECK(j_const.value("/not/existing"_json_pointer, false) == false);
-                    CHECK(j_const.value("/not/existing"_json_pointer, "bar") == "bar");
-                    CHECK(j_const.value("/not/existing"_json_pointer, 12.34) == Approx(12.34));
-                    CHECK(j_const.value("/not/existing"_json_pointer, json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
-                    CHECK(j_const.value("/not/existing"_json_pointer, json({10, 100})) == json({10, 100}));
-                }
-
                 SECTION("access on non-object type")
                 {
                     SECTION("null")
@@ -957,3 +938,37 @@
         }
     }
 }
+
+TEST_CASE("element access 2 (throwing tests)", "[!throws]")
+{
+    SECTION("object")
+    {
+        json j = {{"integer", 1}, {"unsigned", 1u}, {"floating", 42.23}, {"null", nullptr}, {"string", "hello world"}, {"boolean", true}, {"object", json::object()}, {"array", {1, 2, 3}}};
+        const json j_const = j;
+
+        SECTION("access specified element with default value")
+        {
+            SECTION("given a JSON pointer")
+            {
+                SECTION("access non-existing value")
+                {
+                    CHECK(j.value("/not/existing"_json_pointer, 2) == 2);
+                    CHECK(j.value("/not/existing"_json_pointer, 2u) == 2u);
+                    CHECK(j.value("/not/existing"_json_pointer, false) == false);
+                    CHECK(j.value("/not/existing"_json_pointer, "bar") == "bar");
+                    CHECK(j.value("/not/existing"_json_pointer, 12.34) == Approx(12.34));
+                    CHECK(j.value("/not/existing"_json_pointer, json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
+                    CHECK(j.value("/not/existing"_json_pointer, json({10, 100})) == json({10, 100}));
+
+                    CHECK(j_const.value("/not/existing"_json_pointer, 2) == 2);
+                    CHECK(j_const.value("/not/existing"_json_pointer, 2u) == 2u);
+                    CHECK(j_const.value("/not/existing"_json_pointer, false) == false);
+                    CHECK(j_const.value("/not/existing"_json_pointer, "bar") == "bar");
+                    CHECK(j_const.value("/not/existing"_json_pointer, 12.34) == Approx(12.34));
+                    CHECK(j_const.value("/not/existing"_json_pointer, json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
+                    CHECK(j_const.value("/not/existing"_json_pointer, json({10, 100})) == json({10, 100}));
+                }
+            }
+        }
+    }
+}
diff --git a/test/src/unit-inspection.cpp b/test/src/unit-inspection.cpp
index 9e11471..2e3d075 100644
--- a/test/src/unit-inspection.cpp
+++ b/test/src/unit-inspection.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-iterator_wrapper.cpp b/test/src/unit-iterator_wrapper.cpp
index f4255f9..79def90 100644
--- a/test/src/unit-iterator_wrapper.cpp
+++ b/test/src/unit-iterator_wrapper.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-iterators1.cpp b/test/src/unit-iterators1.cpp
index a6fd2df..78ea3db 100644
--- a/test/src/unit-iterators1.cpp
+++ b/test/src/unit-iterators1.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-iterators2.cpp b/test/src/unit-iterators2.cpp
index 6f1b625..ef806ad 100644
--- a/test/src/unit-iterators2.cpp
+++ b/test/src/unit-iterators2.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-json_patch.cpp b/test/src/unit-json_patch.cpp
index b798750..837691d 100644
--- a/test/src/unit-json_patch.cpp
+++ b/test/src/unit-json_patch.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp
index 6725794..24aa4a7 100644
--- a/test/src/unit-json_pointer.cpp
+++ b/test/src/unit-json_pointer.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-meta.cpp b/test/src/unit-meta.cpp
new file mode 100644
index 0000000..28c1a9d
--- /dev/null
+++ b/test/src/unit-meta.cpp
@@ -0,0 +1,40 @@
+/*
+    __ _____ _____ _____
+ __|  |   __|     |   | |  JSON for Modern C++ (test suite)
+|  |  |__   |  |  | | | |  version 2.1.0
+|_____|_____|_____|_|___|  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 "catch.hpp"
+
+#include "json.hpp"
+using nlohmann::json;
+
+TEST_CASE("version information")
+{
+    SECTION("version()")
+    {
+        CHECK(json::meta()["name"] == "JSON for Modern C++");
+    }
+}
diff --git a/test/src/unit-modifiers.cpp b/test/src/unit-modifiers.cpp
index 3e8d960..80b1a71 100644
--- a/test/src/unit-modifiers.cpp
+++ b/test/src/unit-modifiers.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-msgpack.cpp b/test/src/unit-msgpack.cpp
index 89fa450..cad78ae 100644
--- a/test/src/unit-msgpack.cpp
+++ b/test/src/unit-msgpack.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-noexcept.cpp b/test/src/unit-noexcept.cpp
new file mode 100644
index 0000000..ddd8102
--- /dev/null
+++ b/test/src/unit-noexcept.cpp
@@ -0,0 +1,59 @@
+/*
+    __ _____ _____ _____
+ __|  |   __|     |   | |  JSON for Modern C++ (test suite)
+|  |  |__   |  |  | | | |  version 2.1.0
+|_____|_____|_____|_|___|  https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 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 "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{})), "");
diff --git a/test/src/unit-pointer_access.cpp b/test/src/unit-pointer_access.cpp
index 4771c50..5611a75 100644
--- a/test/src/unit-pointer_access.cpp
+++ b/test/src/unit-pointer_access.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-readme.cpp b/test/src/unit-readme.cpp
index 5c62e85..061aadb 100644
--- a/test/src/unit-readme.cpp
+++ b/test/src/unit-readme.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-reference_access.cpp b/test/src/unit-reference_access.cpp
index 54db1a8..6281572 100644
--- a/test/src/unit-reference_access.cpp
+++ b/test/src/unit-reference_access.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp
index 401867c..17edbd5 100644
--- a/test/src/unit-regression.cpp
+++ b/test/src/unit-regression.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -63,10 +63,18 @@
 
     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}
@@ -663,4 +671,31 @@
         std::vector<uint8_t> vec3 {0xbf, 0x61, 0x61, 0x01};
         CHECK_THROWS_AS(json::from_cbor(vec3), std::out_of_range);
     }
+
+    SECTION("issue #416 - Use-of-uninitialized-value (OSS-Fuzz issue 377)")
+    {
+        // original test case
+        std::vector<uint8_t> vec1
+        {
+            0x94, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+            0x3a, 0x96, 0x96, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+            0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0x71,
+            0xb4, 0xb4, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0x3a,
+            0x96, 0x96, 0xb4, 0xb4, 0xfa, 0x94, 0x94, 0x61,
+            0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0xfa
+        };
+        CHECK_THROWS_AS(json::from_cbor(vec1), std::out_of_range);
+
+        // related test case: double-precision
+        std::vector<uint8_t> vec2
+        {
+            0x94, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+            0x3a, 0x96, 0x96, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+            0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0x71,
+            0xb4, 0xb4, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0x3a,
+            0x96, 0x96, 0xb4, 0xb4, 0xfa, 0x94, 0x94, 0x61,
+            0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0xfb
+        };
+        CHECK_THROWS_AS(json::from_cbor(vec2), std::out_of_range);
+    }
 }
diff --git a/test/src/unit-serialization.cpp b/test/src/unit-serialization.cpp
index 72d9ae6..b8bb645 100644
--- a/test/src/unit-serialization.cpp
+++ b/test/src/unit-serialization.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-testsuites.cpp b/test/src/unit-testsuites.cpp
index a43e199..9a9de12 100644
--- a/test/src/unit-testsuites.cpp
+++ b/test/src/unit-testsuites.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -815,3 +815,14 @@
         }
     }
 }
+
+TEST_CASE("Big List of Naughty Strings")
+{
+    // test from https://github.com/minimaxir/big-list-of-naughty-strings
+    SECTION("blns.json")
+    {
+        std::ifstream f("test/data/big-list-of-naughty-strings/blns.json");
+        json j;
+        CHECK_NOTHROW(j << f);
+    }
+}
diff --git a/test/src/unit-udt.cpp b/test/src/unit-udt.cpp
new file mode 100644
index 0000000..347ea37
--- /dev/null
+++ b/test/src/unit-udt.cpp
@@ -0,0 +1,680 @@
+/*
+    __ _____ _____ _____
+ __|  |   __|     |   | |  JSON for Modern C++ (test suite)
+|  |  |__   |  |  | | | |  version 2.1.0
+|_____|_____|_____|_|___|  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>());
+}
diff --git a/test/src/unit-unicode.cpp b/test/src/unit-unicode.cpp
index 0b1c0e5..89e828f 100644
--- a/test/src/unit-unicode.cpp
+++ b/test/src/unit-unicode.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit.cpp b/test/src/unit.cpp
index 096a297..c89a0e8 100644
--- a/test/src/unit.cpp
+++ b/test/src/unit.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.10
+|  |  |__   |  |  | | | |  version 2.1.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/thirdparty/catch/catch.hpp b/test/thirdparty/catch/catch.hpp
old mode 100755
new mode 100644
index 3d18ead..1d49e73
--- a/test/thirdparty/catch/catch.hpp
+++ b/test/thirdparty/catch/catch.hpp
@@ -1,6 +1,6 @@
 /*
- *  Catch v1.5.9
- *  Generated: 2016-11-29 12:14:38.049276
+ *  Catch v1.6.0
+ *  Generated: 2017-01-11 16:38:09.405017
  *  ----------------------------------------------------------
  *  This file has been merged from multiple headers. Please don't edit it directly
  *  Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
@@ -60,21 +60,6 @@
 // #included from: catch_common.h
 #define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED
 
-#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
-#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
-#ifdef CATCH_CONFIG_COUNTER
-#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
-#else
-#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
-#endif
-
-#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr
-#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr )
-
-#include <sstream>
-#include <stdexcept>
-#include <algorithm>
-
 // #included from: catch_compiler_capabilities.h
 #define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
 
@@ -181,6 +166,7 @@
 #if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
 #define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
 #define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE
 #endif
 
 #endif // _MSC_VER
@@ -246,6 +232,9 @@
 #  if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR)
 #    define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
 #  endif
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE)
+#   define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE
+#  endif
 
 #endif // __cplusplus >= 201103L
 
@@ -268,18 +257,21 @@
 #if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS)
 #   define CATCH_CONFIG_VARIADIC_MACROS
 #endif
-#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11)
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11)
 #   define CATCH_CONFIG_CPP11_LONG_LONG
 #endif
-#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11)
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11)
 #   define CATCH_CONFIG_CPP11_OVERRIDE
 #endif
-#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11)
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11)
 #   define CATCH_CONFIG_CPP11_UNIQUE_PTR
 #endif
 #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
 #   define CATCH_CONFIG_COUNTER
 #endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_SHUFFLE
+#endif
 
 #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
 #   define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
@@ -315,6 +307,21 @@
 #   define CATCH_AUTO_PTR( T ) std::auto_ptr<T>
 #endif
 
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
+#ifdef CATCH_CONFIG_COUNTER
+#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
+#else
+#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+#endif
+
+#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr
+#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr )
+
+#include <sstream>
+#include <stdexcept>
+#include <algorithm>
+
 namespace Catch {
 
     struct IConfig;
@@ -2668,6 +2675,26 @@
             return !operator==( rhs, lhs );
         }
 
+        friend bool operator <= ( double lhs, Approx const& rhs )
+        {
+          return lhs < rhs.m_value || lhs == rhs;
+        }
+
+        friend bool operator <= ( Approx const& lhs, double rhs )
+        {
+          return lhs.m_value < rhs || lhs == rhs;
+        }
+
+        friend bool operator >= ( double lhs, Approx const& rhs )
+        {
+          return lhs > rhs.m_value || lhs == rhs;
+        }
+
+        friend bool operator >= ( Approx const& lhs, double rhs )
+        {
+          return lhs.m_value > rhs || lhs == rhs;
+        }
+
         Approx& epsilon( double newEpsilon ) {
             m_epsilon = newEpsilon;
             return *this;
@@ -3257,11 +3284,12 @@
 namespace Catch {
 
     class TestSpecParser {
-        enum Mode{ None, Name, QuotedName, Tag };
+        enum Mode{ None, Name, QuotedName, Tag, EscapedName };
         Mode m_mode;
         bool m_exclusion;
         std::size_t m_start, m_pos;
         std::string m_arg;
+        std::vector<std::size_t> m_escapeChars;
         TestSpec::Filter m_currentFilter;
         TestSpec m_testSpec;
         ITagAliasRegistry const* m_tagAliases;
@@ -3274,6 +3302,7 @@
             m_exclusion = false;
             m_start = std::string::npos;
             m_arg = m_tagAliases->expandAliases( arg );
+            m_escapeChars.clear();
             for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
                 visitChar( m_arg[m_pos] );
             if( m_mode == Name )
@@ -3292,6 +3321,7 @@
                 case '~': m_exclusion = true; return;
                 case '[': return startNewMode( Tag, ++m_pos );
                 case '"': return startNewMode( QuotedName, ++m_pos );
+                case '\\': return escape();
                 default: startNewMode( Name, m_pos ); break;
                 }
             }
@@ -3307,7 +3337,11 @@
                         addPattern<TestSpec::NamePattern>();
                     startNewMode( Tag, ++m_pos );
                 }
+                else if( c == '\\' )
+                    escape();
             }
+            else if( m_mode == EscapedName )
+                m_mode = Name;
             else if( m_mode == QuotedName && c == '"' )
                 addPattern<TestSpec::NamePattern>();
             else if( m_mode == Tag && c == ']' )
@@ -3317,10 +3351,17 @@
             m_mode = mode;
             m_start = start;
         }
+        void escape() {
+            m_mode = EscapedName;
+            m_escapeChars.push_back( m_pos );
+        }
         std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
         template<typename T>
         void addPattern() {
             std::string token = subString();
+            for( size_t i = 0; i < m_escapeChars.size(); ++i )
+                token = token.substr( 0, m_escapeChars[i] ) + token.substr( m_escapeChars[i]+1 );
+            m_escapeChars.clear();
             if( startsWith( token, "exclude:" ) ) {
                 m_exclusion = true;
                 token = token.substr( 8 );
@@ -6458,10 +6499,6 @@
 #include <iostream>
 #include <algorithm>
 
-#ifdef CATCH_CPP14_OR_GREATER
-#include <random>
-#endif
-
 namespace Catch {
 
     struct RandomNumberGenerator {
@@ -6469,7 +6506,7 @@
 
         result_type operator()( result_type n ) const { return std::rand() % n; }
 
-#ifdef CATCH_CPP14_OR_GREATER
+#ifdef CATCH_CONFIG_CPP11_SHUFFLE
         static constexpr result_type min() { return 0; }
         static constexpr result_type max() { return 1000000; }
         result_type operator()() const { return std::rand() % max(); }
@@ -6477,7 +6514,7 @@
         template<typename V>
         static void shuffle( V& vector ) {
             RandomNumberGenerator rng;
-#ifdef CATCH_CPP14_OR_GREATER
+#ifdef CATCH_CONFIG_CPP11_SHUFFLE
             std::shuffle( vector.begin(), vector.end(), rng );
 #else
             std::random_shuffle( vector.begin(), vector.end(), rng );
@@ -7147,7 +7184,7 @@
                 case Colour::White:     return setColour( "[0m" );
                 case Colour::Red:       return setColour( "[0;31m" );
                 case Colour::Green:     return setColour( "[0;32m" );
-                case Colour::Blue:      return setColour( "[0:34m" );
+                case Colour::Blue:      return setColour( "[0;34m" );
                 case Colour::Cyan:      return setColour( "[0;36m" );
                 case Colour::Yellow:    return setColour( "[0;33m" );
                 case Colour::Grey:      return setColour( "[1;30m" );
@@ -7582,7 +7619,7 @@
         return os;
     }
 
-    Version libraryVersion( 1, 5, 9, "", 0 );
+    Version libraryVersion( 1, 6, 0, "", 0 );
 
 }
 
@@ -8349,7 +8386,7 @@
     }
     std::string ResultBuilder::reconstructExpression() const {
         if( m_exprComponents.op == "" )
-            return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.op + m_exprComponents.lhs;
+            return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.lhs;
         else if( m_exprComponents.op == "matches" )
             return m_exprComponents.lhs + " " + m_exprComponents.rhs;
         else if( m_exprComponents.op != "!" ) {
@@ -8967,7 +9004,7 @@
                     default:
                         // Escape control chars - based on contribution by @espenalb in PR #465 and
                         // by @mrpi PR #588
-                        if ( ( c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' )
+                        if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' )
                             os << "&#x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>( c ) << ';';
                         else
                             os << c;
@@ -10400,7 +10437,7 @@
 #define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" )
 #define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" )
 
-#define CATCH_CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" )
+#define CATCH_CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "", "CATCH_CHECK_THROWS" )
 #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" )
 #define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CATCH_CHECK_THROWS_WITH" )
 #define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" )