fix issue#1989: only consider continuous numbers

This commit is contained in:
chenguoping 2020-05-18 16:43:39 +08:00
parent 16d78a82e0
commit 00cbf8a105
3 changed files with 171 additions and 48 deletions

View File

@ -395,20 +395,6 @@ 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
@ -416,13 +402,6 @@ class json_pointer
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
@ -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
@ -928,7 +958,7 @@ class json_pointer
json_pointer(element.first).get_and_create(result) = element.second;
}
return result;
return unflatten_to_array(result);
}
/*!

View File

@ -11228,20 +11228,6 @@ 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
@ -11249,13 +11235,6 @@ class json_pointer
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
@ -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
@ -11761,7 +11791,7 @@ class json_pointer
json_pointer(element.first).get_and_create(result) = element.second;
}
return result;
return unflatten_to_array(result);
}
/*!

View File

@ -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}));
@ -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 :