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
Apr 13, 2016
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
...
...
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
...
...
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'"
);
}
}
}
...
...
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