Skip to content

Commit

Permalink
fix issue#1989: only consider continuous numbers
Browse files Browse the repository at this point in the history
  • Loading branch information
dota17 committed May 18, 2020
1 parent 16d78a8 commit 00cbf8a
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 48 deletions.
74 changes: 52 additions & 22 deletions include/nlohmann/detail/json_pointer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -395,34 +395,13 @@ class json_pointer
switch (result->type())
{
case detail::value_t::null:
{
if (reference_token == "0")
{
// start a new array if reference token is 0
result = &result->operator[](0);
}
else
{
// start a new object otherwise
result = &result->operator[](reference_token);
}
break;
}

case detail::value_t::object:
{
// create an entry in the object
result = &result->operator[](reference_token);
break;
}

case detail::value_t::array:
{
// create an entry in the array
result = &result->operator[](static_cast<size_type>(array_index(reference_token)));
break;
}

/*
The following code is only reached if there exists a reference
token _and_ the current value is primitive. In this case, we have
Expand All @@ -437,6 +416,57 @@ class json_pointer
return *result;
}

/*!
@brief unflatten from object-type JSON to array-type JSON when the keys are continuous numbers
@param[in] j unflattened JSON with non-array
@return unflattened JSON
@throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
static BasicJsonType unflatten_to_array(BasicJsonType& j)
{
// check the keys of object-type value are continuous numbres or not
static auto is_continuous_numbers = [](const BasicJsonType & j) -> bool
{
std::size_t index = 0;
for (auto& item : j.items())
{
if (std::to_string(index) != item.key())
{
return false;
}
index++;
}
return true;
};

if (j.type() != detail::value_t::object)
{
return j;
}

using size_type = typename BasicJsonType::size_type;
bool keys_are_continuous_numbers = is_continuous_numbers(j);
BasicJsonType result;
for (auto& item : j.items())
{
if (keys_are_continuous_numbers)
{
// convert array index to number; unchecked access
result.operator[](static_cast<size_type>(array_index(item.key()))) = unflatten_to_array(item.value());
}
else
{
result.operator[](item.key()) = unflatten_to_array(item.value());
}
}
return result;
}

/*!
@brief return a reference to the pointed to value
Expand Down Expand Up @@ -928,7 +958,7 @@ class json_pointer
json_pointer(element.first).get_and_create(result) = element.second;
}

return result;
return unflatten_to_array(result);
}

/*!
Expand Down
74 changes: 52 additions & 22 deletions single_include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11228,34 +11228,13 @@ class json_pointer
switch (result->type())
{
case detail::value_t::null:
{
if (reference_token == "0")
{
// start a new array if reference token is 0
result = &result->operator[](0);
}
else
{
// start a new object otherwise
result = &result->operator[](reference_token);
}
break;
}

case detail::value_t::object:
{
// create an entry in the object
result = &result->operator[](reference_token);
break;
}

case detail::value_t::array:
{
// create an entry in the array
result = &result->operator[](static_cast<size_type>(array_index(reference_token)));
break;
}

/*
The following code is only reached if there exists a reference
token _and_ the current value is primitive. In this case, we have
Expand All @@ -11270,6 +11249,57 @@ class json_pointer
return *result;
}

/*!
@brief unflatten from object-type JSON to array-type JSON when the keys are continuous numbers

@param[in] j unflattened JSON with non-array

@return unflattened JSON

@throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
static BasicJsonType unflatten_to_array(BasicJsonType& j)
{
// check the keys of object-type value are continuous numbres or not
static auto is_continuous_numbers = [](const BasicJsonType & j) -> bool
{
std::size_t index = 0;
for (auto& item : j.items())
{
if (std::to_string(index) != item.key())
{
return false;
}
index++;
}
return true;
};

if (j.type() != detail::value_t::object)
{
return j;
}

using size_type = typename BasicJsonType::size_type;
bool keys_are_continuous_numbers = is_continuous_numbers(j);
BasicJsonType result;
for (auto& item : j.items())
{
if (keys_are_continuous_numbers)
{
// convert array index to number; unchecked access
result.operator[](static_cast<size_type>(array_index(item.key()))) = unflatten_to_array(item.value());
}
else
{
result.operator[](item.key()) = unflatten_to_array(item.value());
}
}
return result;
}

/*!
@brief return a reference to the pointed to value

Expand Down Expand Up @@ -11761,7 +11791,7 @@ class json_pointer
json_pointer(element.first).get_and_create(result) = element.second;
}

return result;
return unflatten_to_array(result);
}

/*!
Expand Down
71 changes: 67 additions & 4 deletions test/src/unit-json_pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,10 +367,6 @@ TEST_CASE("JSON pointers")
CHECK(not j_const.contains("/one"_json_pointer));
CHECK(not j_const.contains("/one"_json_pointer));

CHECK_THROWS_AS(json({{"/list/0", 1}, {"/list/1", 2}, {"/list/three", 3}}).unflatten(), json::parse_error&);
CHECK_THROWS_WITH(json({{"/list/0", 1}, {"/list/1", 2}, {"/list/three", 3}}).unflatten(),
"[json.exception.parse_error.109] parse error: array index 'three' is not a number");

// assign to "-"
j["/-"_json_pointer] = 99;
CHECK(j == json({1, 13, 3, 33, nullptr, 55, 99}));
Expand Down Expand Up @@ -509,6 +505,73 @@ TEST_CASE("JSON pointers")
CHECK(j_object.flatten().unflatten() == json());
}

SECTION("unflatten")
{
json j =
{
{
"object1", {
{"0", 0},
{"1", 1},
{"2", 2},
}
},
{
"object2", {
{"0", 0},
{"1", 1},
{"two", 2},
}
},
{
"object3", {
{"0", 0},
{"1", 1},
{"3", 3},
}
}
};

json j_flatten =
{
{"/object1/0", 0},
{"/object1/1", 1},
{"/object1/2", 2},
{"/object2/0", 0},
{"/object2/1", 1},
{"/object2/two", 2},
{"/object3/0", 0},
{"/object3/1", 1},
{"/object3/3", 3},
};

json j_unflatten =
{
{"object1", {0, 1, 2}},
{
"object2", {
{"0", 0},
{"1", 1},
{"two", 2},
}
},
{
"object3", {
{"0", 0},
{"1", 1},
{"3", 3},
}
}
};

// check if flattened result is as expected
CHECK(j.flatten() == j_flatten);
CHECK(j_unflatten.flatten() == j_flatten);

// check if unflattened result is as expected
CHECK(j_flatten.unflatten() == j_unflatten);
}

SECTION("string representation")
{
for (auto ptr :
Expand Down

0 comments on commit 00cbf8a

Please sign in to comment.