Skip to content

Commit

Permalink
started fixing #323
Browse files Browse the repository at this point in the history
  • Loading branch information
nlohmann committed Oct 8, 2016
1 parent a0ef5a1 commit 2fa8ea0
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 15 deletions.
2 changes: 1 addition & 1 deletion doc/examples/operatorjson_pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
49 changes: 43 additions & 6 deletions src/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9436,6 +9436,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
Expand All @@ -9450,6 +9456,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:
Expand All @@ -9461,12 +9473,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
Expand All @@ -9480,6 +9486,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 + "'");
Expand Down
49 changes: 43 additions & 6 deletions src/json.hpp.re2c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand All @@ -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
Expand All @@ -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 + "'");
Expand Down
9 changes: 7 additions & 2 deletions test/src/unit-json_pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));

Expand Down
7 changes: 7 additions & 0 deletions test/src/unit-regression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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}}}}));
}
}

0 comments on commit 2fa8ea0

Please sign in to comment.