mirror of
https://github.com/tristanpenman/valijson.git
synced 2024-12-13 10:32:58 +01:00
Allow for redundant slashes in reference tokens, as per spec
This commit is contained in:
parent
b4d7f76b25
commit
4f88bb8941
@ -41,31 +41,39 @@ template<typename AdapterType>
|
|||||||
inline AdapterType resolveJsonPointer(
|
inline AdapterType resolveJsonPointer(
|
||||||
const AdapterType &node,
|
const AdapterType &node,
|
||||||
const std::string &jsonPointer,
|
const std::string &jsonPointer,
|
||||||
std::string::const_iterator jsonPointerItr)
|
const std::string::const_iterator jsonPointerItr)
|
||||||
{
|
{
|
||||||
// TODO: This function will probably need to implement support for
|
// TODO: This function will probably need to implement support for
|
||||||
// fetching documents referenced by JSON Pointers, similar to the
|
// fetching documents referenced by JSON Pointers, similar to the
|
||||||
// populateSchema function.
|
// populateSchema function.
|
||||||
|
|
||||||
const std::string::const_iterator jsonPointerEnd = jsonPointer.end();
|
const std::string::const_iterator jsonPointerEnd = jsonPointer.end();
|
||||||
|
|
||||||
|
// Terminate recursion if all reference tokens have been consumed
|
||||||
if (jsonPointerItr == jsonPointerEnd) {
|
if (jsonPointerItr == jsonPointerEnd) {
|
||||||
// Bottom out recursion
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find iterator that points to next slash, or end of string
|
// Reference tokens must begin with a leading slash
|
||||||
const std::string::const_iterator jsonPointerNext =
|
if (*jsonPointerItr != '/') {
|
||||||
std::find(jsonPointerItr, jsonPointerEnd, '/');
|
throw std::runtime_error("Expected reference token to begin with "
|
||||||
|
"leading slash; remaining tokens: " +
|
||||||
// Extract the next reference token
|
std::string(jsonPointerItr, jsonPointerEnd));
|
||||||
const std::string referenceToken(jsonPointerItr, jsonPointerNext);
|
|
||||||
if (referenceToken.empty()) {
|
|
||||||
throw std::runtime_error(
|
|
||||||
"Expected at least one non-delimiting character in the next "
|
|
||||||
"reference token.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.isArray()) {
|
// Find iterator that points to next slash or newline character; this is
|
||||||
|
// one character past the end of the current reference token
|
||||||
|
std::string::const_iterator jsonPointerNext =
|
||||||
|
std::find(jsonPointerItr + 1, jsonPointerEnd, '/');
|
||||||
|
|
||||||
|
// Extract the next reference token
|
||||||
|
const std::string referenceToken(jsonPointerItr + 1, jsonPointerNext);
|
||||||
|
|
||||||
|
// Empty reference tokens should be ignored
|
||||||
|
if (referenceToken.empty()) {
|
||||||
|
return resolveJsonPointer(node, jsonPointer, jsonPointerNext);
|
||||||
|
|
||||||
|
} else if (node.isArray()) {
|
||||||
try {
|
try {
|
||||||
// Fragment must be non-negative integer
|
// Fragment must be non-negative integer
|
||||||
const uint64_t index = boost::lexical_cast<uint64_t>(jsonPointer);
|
const uint64_t index = boost::lexical_cast<uint64_t>(jsonPointer);
|
||||||
@ -87,7 +95,8 @@ inline AdapterType resolveJsonPointer(
|
|||||||
} else if (node.maybeObject()) {
|
} else if (node.maybeObject()) {
|
||||||
// Fragment must identify a member of the candidate object
|
// Fragment must identify a member of the candidate object
|
||||||
typedef typename AdapterType::Object Object;
|
typedef typename AdapterType::Object Object;
|
||||||
typename Object::const_iterator itr = node.asObject().find(referenceToken);
|
typename Object::const_iterator itr = node.asObject().find(
|
||||||
|
referenceToken);
|
||||||
if (itr == node.asObject().end()) {
|
if (itr == node.asObject().end()) {
|
||||||
throw std::runtime_error("Expected reference token to identify an "
|
throw std::runtime_error("Expected reference token to identify an "
|
||||||
"element in the current object; "
|
"element in the current object; "
|
||||||
@ -145,12 +154,7 @@ inline AdapterType resolveJsonPointer(
|
|||||||
const AdapterType &rootNode,
|
const AdapterType &rootNode,
|
||||||
const std::string &jsonPointer)
|
const std::string &jsonPointer)
|
||||||
{
|
{
|
||||||
if (jsonPointer.find("/") != 0) {
|
return ::resolveJsonPointer(rootNode, jsonPointer, jsonPointer.begin());
|
||||||
throw std::runtime_error(
|
|
||||||
"Expected leading '/' while parsing JSON Pointer.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return ::resolveJsonPointer(rootNode, jsonPointer, jsonPointer.begin() + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace json_reference
|
} // namespace json_reference
|
||||||
|
@ -52,10 +52,24 @@ std::vector<boost::shared_ptr<JsonPointerTestCase> >
|
|||||||
testCases.push_back(testCase);
|
testCases.push_back(testCase);
|
||||||
|
|
||||||
testCase = boost::make_shared<JsonPointerTestCase>(
|
testCase = boost::make_shared<JsonPointerTestCase>(
|
||||||
"Resolving an empty string should cause an exception to be thrown");
|
"Resolving an empty string should return the root node");
|
||||||
testCase->value.SetNull();
|
testCase->value.SetNull();
|
||||||
testCase->jsonPointer = "";
|
testCase->jsonPointer = "";
|
||||||
testCase->expectedValue = NULL;
|
testCase->expectedValue = &testCase->value;
|
||||||
|
testCases.push_back(testCase);
|
||||||
|
|
||||||
|
testCase = boost::make_shared<JsonPointerTestCase>(
|
||||||
|
"Resolving '/' should return the root node");
|
||||||
|
testCase->value.SetNull();
|
||||||
|
testCase->jsonPointer = "/";
|
||||||
|
testCase->expectedValue = &testCase->value;
|
||||||
|
testCases.push_back(testCase);
|
||||||
|
|
||||||
|
testCase = boost::make_shared<JsonPointerTestCase>(
|
||||||
|
"Resolving '//' should return the root node");
|
||||||
|
testCase->value.SetNull();
|
||||||
|
testCase->jsonPointer = "//";
|
||||||
|
testCase->expectedValue = &testCase->value;
|
||||||
testCases.push_back(testCase);
|
testCases.push_back(testCase);
|
||||||
|
|
||||||
testCase = boost::make_shared<JsonPointerTestCase>(
|
testCase = boost::make_shared<JsonPointerTestCase>(
|
||||||
@ -67,12 +81,19 @@ std::vector<boost::shared_ptr<JsonPointerTestCase> >
|
|||||||
testCases.push_back(testCase);
|
testCases.push_back(testCase);
|
||||||
|
|
||||||
testCase = boost::make_shared<JsonPointerTestCase>(
|
testCase = boost::make_shared<JsonPointerTestCase>(
|
||||||
"Resolve '/test/' in object containing one member named 'test' "
|
"Resolve '/test/' in object containing one member named 'test'");
|
||||||
"should cause an exception to be thrown");
|
|
||||||
testCase->value.SetObject();
|
testCase->value.SetObject();
|
||||||
testCase->value.AddMember("test", "test", allocator);
|
testCase->value.AddMember("test", "test", allocator);
|
||||||
testCase->jsonPointer = "/test/";
|
testCase->jsonPointer = "/test/";
|
||||||
testCase->expectedValue = NULL;
|
testCase->expectedValue = &testCase->value.FindMember("test")->value;
|
||||||
|
testCases.push_back(testCase);
|
||||||
|
|
||||||
|
testCase = boost::make_shared<JsonPointerTestCase>(
|
||||||
|
"Resolve '//test//' in object containing one member named 'test'");
|
||||||
|
testCase->value.SetObject();
|
||||||
|
testCase->value.AddMember("test", "test", allocator);
|
||||||
|
testCase->jsonPointer = "//test//";
|
||||||
|
testCase->expectedValue = &testCase->value.FindMember("test")->value;
|
||||||
testCases.push_back(testCase);
|
testCases.push_back(testCase);
|
||||||
|
|
||||||
testCase = boost::make_shared<JsonPointerTestCase>(
|
testCase = boost::make_shared<JsonPointerTestCase>(
|
||||||
@ -108,7 +129,8 @@ TEST_F(TestJsonReference, JsonPointerTestCases)
|
|||||||
} else {
|
} else {
|
||||||
EXPECT_THROW(
|
EXPECT_THROW(
|
||||||
resolveJsonPointer(valueAdapter, jsonPointer),
|
resolveJsonPointer(valueAdapter, jsonPointer),
|
||||||
std::runtime_error);
|
std::runtime_error) <<
|
||||||
|
(*itr)->description;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user