From 2fa8ea0f747538091bafb311cd3c843778b15817 Mon Sep 17 00:00:00 2001
From: Niels <niels.lohmann@gmail.com>
Date: Sat, 8 Oct 2016 14:27:28 +0200
Subject: [PATCH 1/5] started fixing #323

---
 doc/examples/operatorjson_pointer.cpp |  2 +-
 src/json.hpp                          | 49 +++++++++++++++++++++++----
 src/json.hpp.re2c                     | 49 +++++++++++++++++++++++----
 test/src/unit-json_pointer.cpp        |  9 +++--
 test/src/unit-regression.cpp          |  7 ++++
 5 files changed, 101 insertions(+), 15 deletions(-)

diff --git a/doc/examples/operatorjson_pointer.cpp b/doc/examples/operatorjson_pointer.cpp
index 18e41c1f..d7e8faff 100644
--- a/doc/examples/operatorjson_pointer.cpp
+++ b/doc/examples/operatorjson_pointer.cpp
@@ -40,7 +40,7 @@ int main()
     // output the changed array
     std::cout << j["array"] << '\n';
 
-    // "change" the arry element past the end
+    // "change" the array element past the end
     j["/array/-"_json_pointer] = 55;
     // output the changed array
     std::cout << j["array"] << '\n';
diff --git a/src/json.hpp b/src/json.hpp
index 04c8573f..1cbcedf6 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -9436,6 +9436,12 @@ basic_json_parser_63:
         /*!
         @brief return a reference to the pointed to value
 
+        @note This version does not throw if a value is not present, but tries
+        to create nested values instead. For instance, calling this function
+        with pointer `"/this/that"` on a null value is equivalent to calling
+        `operator[]("this").operator[]("that")` on that value, effectively
+        changing the null value to an object.
+
         @param[in] ptr  a JSON value
 
         @return reference to the JSON value pointed to by the JSON pointer
@@ -9450,6 +9456,12 @@ basic_json_parser_63:
         {
             for (const auto& reference_token : reference_tokens)
             {
+                // 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'");
+                }
+
                 switch (ptr->m_type)
                 {
                     case value_t::object:
@@ -9461,12 +9473,6 @@ basic_json_parser_63:
 
                     case value_t::array:
                     {
-                        // 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'");
-                        }
-
                         if (reference_token == "-")
                         {
                             // explicityly treat "-" as index beyond the end
@@ -9480,6 +9486,37 @@ basic_json_parser_63:
                         break;
                     }
 
+                    // null values are converted to arrays or objects
+                    case value_t::null:
+                    {
+                        // check if reference token is a number
+                        const bool nums = std::all_of(reference_token.begin(),
+                                                      reference_token.end(),
+                                                      [](const char x)
+                        {
+                            return std::isdigit(x);
+                        });
+
+                        if (nums)
+                        {
+                            // if reference token consists solely of numbers
+                            // use it as array index -> create array
+                            ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
+                        }
+                        else if (reference_token == "-")
+                        {
+                            // explicityly treat "-" as index beyond the end
+                            // which is 0 for an empty array -> create array
+                            ptr = &ptr->operator[](0);
+                        }
+                        else
+                        {
+                            // treat reference token as key -> create object
+                            ptr = &ptr->operator[](reference_token);
+                        }
+                        break;
+                    }
+
                     default:
                     {
                         throw std::out_of_range("unresolved reference token '" + reference_token + "'");
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index 48224835..2dabc031 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -8733,6 +8733,12 @@ class basic_json
         /*!
         @brief return a reference to the pointed to value
 
+        @note This version does not throw if a value is not present, but tries
+        to create nested values instead. For instance, calling this function
+        with pointer `"/this/that"` on a null value is equivalent to calling
+        `operator[]("this").operator[]("that")` on that value, effectively
+        changing the null value to an object.
+
         @param[in] ptr  a JSON value
 
         @return reference to the JSON value pointed to by the JSON pointer
@@ -8747,6 +8753,12 @@ class basic_json
         {
             for (const auto& reference_token : reference_tokens)
             {
+                // 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'");
+                }
+
                 switch (ptr->m_type)
                 {
                     case value_t::object:
@@ -8758,12 +8770,6 @@ class basic_json
 
                     case value_t::array:
                     {
-                        // 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'");
-                        }
-
                         if (reference_token == "-")
                         {
                             // explicityly treat "-" as index beyond the end
@@ -8777,6 +8783,37 @@ class basic_json
                         break;
                     }
 
+                    // null values are converted to arrays or objects
+                    case value_t::null:
+                    {
+                        // check if reference token is a number
+                        const bool nums = std::all_of(reference_token.begin(),
+                                                      reference_token.end(),
+                                                      [](const char x)
+                        {
+                            return std::isdigit(x);
+                        });
+
+                        if (nums)
+                        {
+                            // if reference token consists solely of numbers
+                            // use it as array index -> create array
+                            ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
+                        }
+                        else if (reference_token == "-")
+                        {
+                            // explicityly treat "-" as index beyond the end
+                            // which is 0 for an empty array -> create array
+                            ptr = &ptr->operator[](0);
+                        }
+                        else
+                        {
+                            // treat reference token as key -> create object
+                            ptr = &ptr->operator[](reference_token);
+                        }
+                        break;
+                    }
+
                     default:
                     {
                         throw std::out_of_range("unresolved reference token '" + reference_token + "'");
diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp
index c7f1708c..892a958f 100644
--- a/test/src/unit-json_pointer.cpp
+++ b/test/src/unit-json_pointer.cpp
@@ -109,8 +109,13 @@ TEST_CASE("JSON pointers")
             CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]);
 
             // unescaped access
-            CHECK_THROWS_AS(j[json::json_pointer("/a/b")], std::out_of_range);
-            CHECK_THROWS_WITH(j[json::json_pointer("/a/b")], "unresolved reference token 'b'");
+            // access to nonexisting values yield object creation
+            CHECK_NOTHROW(j[json::json_pointer("/a/b")] = 42);
+            CHECK(j["a"]["b"] == json(42));
+            CHECK_NOTHROW(j[json::json_pointer("/a/c/1")] = 42);
+            CHECK(j["a"]["c"] == json({nullptr, 42}));
+            CHECK_NOTHROW(j[json::json_pointer("/a/d/-")] = 42);
+            CHECK(j["a"]["d"] == json::array({42}));
             // "/a/b" works for JSON {"a": {"b": 42}}
             CHECK(json({{"a", {{"b", 42}}}})[json::json_pointer("/a/b")] == json(42));
 
diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp
index 34eb197f..6d93e744 100644
--- a/test/src/unit-regression.cpp
+++ b/test/src/unit-regression.cpp
@@ -482,4 +482,11 @@ TEST_CASE("regression tests")
             CHECK_NOTHROW(j << f);
         }
     }
+
+    SECTION("issue #323 - add nested object capabilities to pointers")
+    {
+        json j;
+        j["/this/that"_json_pointer] = 27;
+        CHECK(j == json({{"this", {{"that", 27}}}}));
+    }
 }

From 54bf5f20e99c2912e7102e6e9955cdccf00d2c57 Mon Sep 17 00:00:00 2001
From: Niels <niels.lohmann@gmail.com>
Date: Sat, 8 Oct 2016 14:33:10 +0200
Subject: [PATCH 2/5] adjusted README

---
 README.md | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/README.md b/README.md
index 962e9dda..bc156057 100644
--- a/README.md
+++ b/README.md
@@ -515,17 +515,17 @@ To compile and run the tests, you need to execute
 $ make check
 
 ===============================================================================
-All tests passed (8905161 assertions in 35 test cases)
+All tests passed (8905166 assertions in 35 test cases)
 ```
 
 Alternatively, you can use [https://cmake.org](CMake) and run
 
 ```sh
-mkdir build
-cd build
-cmake ..
-make
-ctest
+$ mkdir build
+$ cd build
+$ cmake ..
+$ make
+$ ctest
 ```
 
 For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml).

From 97280bbcfc6038c5e135d6822d92f71ce0e9bde3 Mon Sep 17 00:00:00 2001
From: Niels <niels.lohmann@gmail.com>
Date: Sun, 9 Oct 2016 16:32:01 +0200
Subject: [PATCH 3/5] added missing header

---
 src/json.hpp      | 1 +
 src/json.hpp.re2c | 1 +
 2 files changed, 2 insertions(+)

diff --git a/src/json.hpp b/src/json.hpp
index 1cbcedf6..0e8eece3 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -32,6 +32,7 @@ SOFTWARE.
 #include <algorithm>
 #include <array>
 #include <cassert>
+#include <cctype>
 #include <ciso646>
 #include <cmath>
 #include <cstddef>
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index 2dabc031..757f8859 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -32,6 +32,7 @@ SOFTWARE.
 #include <algorithm>
 #include <array>
 #include <cassert>
+#include <cctype>
 #include <ciso646>
 #include <cmath>
 #include <cstddef>

From d3e7f9da6797596bae944c8c07ef29398cba3668 Mon Sep 17 00:00:00 2001
From: Niels <niels.lohmann@gmail.com>
Date: Mon, 10 Oct 2016 20:38:50 +0200
Subject: [PATCH 4/5] code cleanup for #323

---
 src/json.hpp      | 61 +++++++++++++++++++++--------------------------
 src/json.hpp.re2c | 61 +++++++++++++++++++++--------------------------
 2 files changed, 54 insertions(+), 68 deletions(-)

diff --git a/src/json.hpp b/src/json.hpp
index 0e8eece3..78cc7093 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -9457,10 +9457,27 @@ basic_json_parser_63:
         {
             for (const auto& reference_token : reference_tokens)
             {
-                // error condition (cf. RFC 6901, Sect. 4)
-                if (reference_token.size() > 1 and reference_token[0] == '0')
+                // convert null values to arrays or objects before continuing
+                if (ptr->m_type == value_t::null)
                 {
-                    throw std::domain_error("array index must not begin with '0'");
+                    // check if reference token is a number
+                    const bool nums = std::all_of(reference_token.begin(),
+                                                  reference_token.end(),
+                                                  [](const char x)
+                    {
+                        return std::isdigit(x);
+                    });
+
+                    // change value to array for numbers or "-" or to object
+                    // otherwise
+                    if (nums or reference_token == "-")
+                    {
+                        *ptr = value_t::array;
+                    }
+                    else
+                    {
+                        *ptr = value_t::object;
+                    }
                 }
 
                 switch (ptr->m_type)
@@ -9474,6 +9491,13 @@ basic_json_parser_63:
 
                     case value_t::array:
                     {
+
+                        // 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'");
+                        }
+
                         if (reference_token == "-")
                         {
                             // explicityly treat "-" as index beyond the end
@@ -9487,37 +9511,6 @@ basic_json_parser_63:
                         break;
                     }
 
-                    // null values are converted to arrays or objects
-                    case value_t::null:
-                    {
-                        // check if reference token is a number
-                        const bool nums = std::all_of(reference_token.begin(),
-                                                      reference_token.end(),
-                                                      [](const char x)
-                        {
-                            return std::isdigit(x);
-                        });
-
-                        if (nums)
-                        {
-                            // if reference token consists solely of numbers
-                            // use it as array index -> create array
-                            ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
-                        }
-                        else if (reference_token == "-")
-                        {
-                            // explicityly treat "-" as index beyond the end
-                            // which is 0 for an empty array -> create array
-                            ptr = &ptr->operator[](0);
-                        }
-                        else
-                        {
-                            // treat reference token as key -> create object
-                            ptr = &ptr->operator[](reference_token);
-                        }
-                        break;
-                    }
-
                     default:
                     {
                         throw std::out_of_range("unresolved reference token '" + reference_token + "'");
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index 757f8859..61196844 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -8754,10 +8754,27 @@ class basic_json
         {
             for (const auto& reference_token : reference_tokens)
             {
-                // error condition (cf. RFC 6901, Sect. 4)
-                if (reference_token.size() > 1 and reference_token[0] == '0')
+                // convert null values to arrays or objects before continuing
+                if (ptr->m_type == value_t::null)
                 {
-                    throw std::domain_error("array index must not begin with '0'");
+                    // check if reference token is a number
+                    const bool nums = std::all_of(reference_token.begin(),
+                                                  reference_token.end(),
+                                                  [](const char x)
+                    {
+                        return std::isdigit(x);
+                    });
+
+                    // change value to array for numbers or "-" or to object
+                    // otherwise
+                    if (nums or reference_token == "-")
+                    {
+                        *ptr = value_t::array;
+                    }
+                    else
+                    {
+                        *ptr = value_t::object;
+                    }
                 }
 
                 switch (ptr->m_type)
@@ -8771,6 +8788,13 @@ class basic_json
 
                     case value_t::array:
                     {
+
+                        // 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'");
+                        }
+
                         if (reference_token == "-")
                         {
                             // explicityly treat "-" as index beyond the end
@@ -8784,37 +8808,6 @@ class basic_json
                         break;
                     }
 
-                    // null values are converted to arrays or objects
-                    case value_t::null:
-                    {
-                        // check if reference token is a number
-                        const bool nums = std::all_of(reference_token.begin(),
-                                                      reference_token.end(),
-                                                      [](const char x)
-                        {
-                            return std::isdigit(x);
-                        });
-
-                        if (nums)
-                        {
-                            // if reference token consists solely of numbers
-                            // use it as array index -> create array
-                            ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
-                        }
-                        else if (reference_token == "-")
-                        {
-                            // explicityly treat "-" as index beyond the end
-                            // which is 0 for an empty array -> create array
-                            ptr = &ptr->operator[](0);
-                        }
-                        else
-                        {
-                            // treat reference token as key -> create object
-                            ptr = &ptr->operator[](reference_token);
-                        }
-                        break;
-                    }
-
                     default:
                     {
                         throw std::out_of_range("unresolved reference token '" + reference_token + "'");

From 470197bd0bfda213ad772b64d1af1b3e70c99380 Mon Sep 17 00:00:00 2001
From: Niels <niels.lohmann@gmail.com>
Date: Mon, 10 Oct 2016 21:58:31 +0200
Subject: [PATCH 5/5] improve test coverage

---
 test/src/unit-deserialization.cpp | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp
index 672f15df..11a2c7a2 100644
--- a/test/src/unit-deserialization.cpp
+++ b/test/src/unit-deserialization.cpp
@@ -238,6 +238,18 @@ TEST_CASE("deserialization")
                 uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u', '1'};
                 CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument);
             }
+
+            SECTION("case 3")
+            {
+                uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u', '1', '1', '1', '1', '1', '1', '1', '1'};
+                CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument);
+            }
+
+            SECTION("case 4")
+            {
+                uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', 'u', '1', '1', '1', '1', '1', '1', '1', '1', '\\'};
+                CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument);
+            }
         }
     }
 }