Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
J
json
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Libraries
json
Commits
94af8abd
Commit
94af8abd
authored
9 years ago
by
Niels
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
overworked reference token parsing
parent
2cb925c1
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
154 additions
and
43 deletions
+154
-43
src/json.hpp
src/json.hpp
+66
-22
src/json.hpp.re2c
src/json.hpp.re2c
+65
-21
test/unit.cpp
test/unit.cpp
+23
-0
No files found.
src/json.hpp
View file @
94af8abd
...
...
@@ -8911,10 +8911,21 @@ basic_json_parser_64:
/// the reference tokens
std
::
vector
<
std
::
string
>
reference_tokens
{};
/// replace all occurrences of a substring by another string
void
replace_substring
(
std
::
string
&
s
,
const
std
::
string
&
f
,
const
std
::
string
&
t
)
/*!
@brief replace all occurrences of a substring by another string
@param[in,out] s the string to manipulate
@param[in] f the substring to replace with @a t
@param[out] t the string to replace @a f
@return The string @a s where all occurrences of @a f are replaced
with @a t.
@pre The search string @f must not be empty.
*/
static
void
replace_substring
(
std
::
string
&
s
,
const
std
::
string
&
f
,
const
std
::
string
&
t
)
{
assert
(
not
f
.
empty
());
...
...
@@ -8941,26 +8952,49 @@ basic_json_parser_64:
throw
std
::
domain_error
(
"JSON pointer must be empty or begin with '/'"
);
}
// tokenize reference string
auto
ptr
=
std
::
strtok
(
&
reference_string
[
0
],
"/"
);
while
(
ptr
!=
nullptr
)
{
reference_tokens
.
push_back
(
ptr
);
ptr
=
std
::
strtok
(
NULL
,
"/"
);
}
// special case: reference string was just "/"
if
(
reference_tokens
.
empty
())
{
reference_tokens
.
push_back
(
""
);
}
// extract the reference tokens:
// - slash: position of the last read slash (or end of string)
// - start: position after the previous slash
for
(
// search for the first slash after the first character
size_t
slash
=
reference_string
.
find_first_of
(
"/"
,
1
),
// set the beginning of the first reference token
start
=
1
;
// we can stop if start == string::npos+1 = 0
start
!=
0
;
// set the beginning of the next reference token
// (could be 0 if slash == std::string::npos)
start
=
slash
+
1
,
// find next slash
slash
=
reference_string
.
find_first_of
(
"/"
,
start
))
{
// use the text between the beginning of the reference token
// (start) and the last slash (slash).
auto
reference_token
=
reference_string
.
substr
(
start
,
slash
-
start
);
// check reference tokens are properly escaped
for
(
size_t
pos
=
reference_token
.
find_first_of
(
"~"
);
pos
!=
std
::
string
::
npos
;
pos
=
reference_token
.
find_first_of
(
"~"
,
pos
+
1
))
{
assert
(
reference_token
[
pos
]
==
'~'
);
// ~ must be followed by 0 or 1
if
(
pos
==
reference_token
.
size
()
-
1
or
(
reference_token
[
pos
+
1
]
!=
'0'
and
reference_token
[
pos
+
1
]
!=
'1'
))
{
throw
std
::
domain_error
(
"escape error: '~' must be followed with '0' or '1'"
);
}
}
for
(
auto
&
reference_token
:
reference_tokens
)
{
// first transform any occurrence of the sequence '~1' to '/'
replace_substring
(
reference_token
,
"~1"
,
"/"
);
// then transform any occurrence of the sequence '~0' to '~'
replace_substring
(
reference_token
,
"~0"
,
"~"
);
// store the reference token
reference_tokens
.
push_back
(
reference_token
);
}
}
};
...
...
@@ -9026,9 +9060,9 @@ struct hash<nlohmann::json>
/*!
@brief user-defined string literal for JSON values
This operator implements a user-defined string literal for JSON objects. It
can
be used by adding \p "_json" to a string literal and returns a JSON object if
no parse error occurred.
This operator implements a user-defined string literal for JSON objects. It
can be used by adding \p "_json" to a string literal and returns a JSON object
if
no parse error occurred.
@param[in] s a string representation of a JSON object
@return a JSON object
...
...
@@ -9040,6 +9074,16 @@ inline nlohmann::json operator "" _json(const char* s, std::size_t)
return
nlohmann
::
json
::
parse
(
reinterpret_cast
<
const
nlohmann
::
json
::
string_t
::
value_type
*>
(
s
));
}
/*!
@brief user-defined string literal for JSON pointer
@since version 2.0.0
*/
inline
nlohmann
::
json
::
json_pointer
operator
""
_json_pointer
(
const
char
*
s
,
std
::
size_t
)
{
return
nlohmann
::
json
::
json_pointer
(
s
);
}
// restore GCC/clang diagnostic settings
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic pop
...
...
This diff is collapsed.
Click to expand it.
src/json.hpp.re2c
View file @
94af8abd
...
...
@@ -8190,10 +8190,21 @@ class basic_json
/// the reference tokens
std::vector<std::string> reference_tokens {};
/// replace all occurrences of a substring by another string
void replace_substring(std::string& s,
const std::string& f,
const std::string& t)
/*!
@brief replace all occurrences of a substring by another string
@param[in,out] s the string to manipulate
@param[in] f the substring to replace with @a t
@param[out] t the string to replace @a f
@return The string @a s where all occurrences of @a f are replaced
with @a t.
@pre The search string @f must not be empty.
*/
static void replace_substring(std::string& s,
const std::string& f,
const std::string& t)
{
assert(not f.empty());
...
...
@@ -8220,26 +8231,49 @@ class basic_json
throw std::domain_error("JSON pointer must be empty or begin with '/'");
}
// tokenize reference string
auto ptr = std::strtok(&reference_string[0], "/");
while (ptr != nullptr)
{
reference_tokens.push_back(ptr);
ptr = std::strtok(NULL, "/");
}
// extract the reference tokens:
// - slash: position of the last read slash (or end of string)
// - start: position after the previous slash
for (
// search for the first slash after the first character
size_t slash = reference_string.find_first_of("/", 1),
// set the beginning of the first reference token
start = 1;
// we can stop if start == string::npos+1 = 0
start != 0;
// set the beginning of the next reference token
// (could be 0 if slash == std::string::npos)
start = slash + 1,
// find next slash
slash = reference_string.find_first_of("/", start))
{
// use the text between the beginning of the reference token
// (start) and the last slash (slash).
auto reference_token = reference_string.substr(start, slash - start);
// check reference tokens are properly escaped
for (size_t pos = reference_token.find_first_of("~");
pos != std::string::npos;
pos = reference_token.find_first_of("~", pos + 1))
{
assert(reference_token[pos] == '~');
// special case: reference string was just "/"
if (reference_tokens.empty())
{
reference_tokens.push_back("");
}
// ~ must be followed by 0 or 1
if (pos == reference_token.size() - 1 or
(reference_token[pos + 1] != '0' and
reference_token[pos + 1] != '1'))
{
throw std::domain_error("escape error: '~' must be followed with '0' or '1'");
}
}
for (auto& reference_token : reference_tokens)
{
// first transform any occurrence of the sequence '~1' to '/'
replace_substring(reference_token, "~1", "/");
// then transform any occurrence of the sequence '~0' to '~'
replace_substring(reference_token, "~0", "~");
// store the reference token
reference_tokens.push_back(reference_token);
}
}
};
...
...
@@ -8305,9 +8339,9 @@ struct hash<nlohmann::json>
/*!
@brief user-defined string literal for JSON values
This operator implements a user-defined string literal for JSON objects. It
can
be used by adding \p "_json" to a string literal and returns a JSON object if
no parse error occurred.
This operator implements a user-defined string literal for JSON objects. It
can be used by adding \p "_json" to a string literal and returns a JSON object
if
no parse error occurred.
@param[in] s a string representation of a JSON object
@return a JSON object
...
...
@@ -8319,6 +8353,16 @@ inline nlohmann::json operator "" _json(const char* s, std::size_t)
return nlohmann::json::parse(reinterpret_cast<const nlohmann::json::string_t::value_type*>(s));
}
/*!
@brief user-defined string literal for JSON pointer
@since version 2.0.0
*/
inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t)
{
return nlohmann::json::json_pointer(s);
}
// restore GCC/clang diagnostic settings
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic pop
...
...
This diff is collapsed.
Click to expand it.
test/unit.cpp
View file @
94af8abd
...
...
@@ -12118,6 +12118,29 @@ TEST_CASE("JSON pointers")
CHECK(j_const[""] == json::json_pointer("/").get(j_const));
CHECK(j_const[" "] == json::json_pointer("/ ").get(j_const));
}
SECTION("user-defined string literal")
{
// the whole document
CHECK(""_json_pointer.get(j) == j);
// array access
CHECK("/foo"_json_pointer.get(j) == j["foo"]);
CHECK("/foo/0"_json_pointer.get(j) == j["foo"][0]);
CHECK("/foo/1"_json_pointer.get(j) == j["foo"][1]);
}
SECTION("errors")
{
CHECK_THROWS_AS(json::json_pointer("foo"), std::domain_error);
CHECK_THROWS_WITH(json::json_pointer("foo"), "JSON pointer must be empty or begin with '/'");
CHECK_THROWS_AS(json::json_pointer("/~~"), std::domain_error);
CHECK_THROWS_WITH(json::json_pointer("/~~"), "escape error: '~' must be followed with '0' or '1'");
CHECK_THROWS_AS(json::json_pointer("/~"), std::domain_error);
CHECK_THROWS_WITH(json::json_pointer("/~"), "escape error: '~' must be followed with '0' or '1'");
}
}
}
...
...
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment