fix issue#1989: only consider continuous numbers
This commit is contained in:
parent
16d78a82e0
commit
00cbf8a105
@ -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);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@ -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 :
|
||||
|
||||
Loading…
Reference in New Issue
Block a user