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
1483d39c
Unverified
Commit
1483d39c
authored
7 years ago
by
Niels Lohmann
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
moved class json_pointer into separate file #920
parent
e95578f8
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
1174 additions
and
1201 deletions
+1174
-1201
Makefile
Makefile
+9
-0
develop/detail/input/binary_reader.hpp
develop/detail/input/binary_reader.hpp
+1
-0
develop/detail/json_pointer.hpp
develop/detail/json_pointer.hpp
+696
-0
develop/detail/output/serializer.hpp
develop/detail/output/serializer.hpp
+3
-0
develop/json.hpp
develop/json.hpp
+3
-725
develop/json_fwd.hpp
develop/json_fwd.hpp
+1
-0
src/json.hpp
src/json.hpp
+461
-476
No files found.
Makefile
View file @
1483d39c
...
...
@@ -16,6 +16,7 @@ SRCS = develop/json.hpp \
develop/detail/iterators/iteration_proxy.hpp
\
develop/detail/iterators/json_reverse_iterator.hpp
\
develop/detail/iterators/primitive_iterator.hpp
\
develop/detail/json_pointer.hpp
\
develop/detail/json_ref.hpp
\
develop/detail/macro_scope.hpp
\
develop/detail/macro_unscope.hpp
\
...
...
@@ -273,6 +274,14 @@ check-amalgamation:
@
diff src/json.hpp src/json.hpp~
||
(
echo
"===================================================================
\n
Amalgamation required! Please read the contribution guidelines
\n
in file .github/CONTRIBUTING.md.
\n
==================================================================="
;
mv
src/json.hpp~ src/json.hpp
;
false
)
@
mv
src/json.hpp~ src/json.hpp
# check if every header in develop includes sufficient headers to be compiled
# individually
check-single-includes
:
for
x
in
$(SRCS)
;
do
\
echo
"#include
\"
$$
x
\"\n
int main() {}
\n
"
|
sed
's|develop/||'
>
single_include_test.cpp
;
\
$(CXX)
$(CXXFLAGS)
-Idevelop
-std
=
c++11 single_include_test.cpp
-o
single_include_test
;
\
rm
single_include_test.cpp single_include_test
;
\
done
##########################################################################
# changelog
...
...
This diff is collapsed.
Click to expand it.
develop/detail/input/binary_reader.hpp
View file @
1483d39c
...
...
@@ -15,6 +15,7 @@
#include <string> // char_traits, string
#include <utility> // make_pair, move
#include "detail/input/input_adapters.hpp"
#include "detail/exceptions.hpp"
#include "detail/macro_scope.hpp"
#include "detail/value_t.hpp"
...
...
This diff is collapsed.
Click to expand it.
develop/detail/json_pointer.hpp
0 → 100644
View file @
1483d39c
#pragma once
#include <cassert> // assert
#include <numeric> // accumulate
#include <string> // string
#include <vector> // vector
#include "detail/macro_scope.hpp"
#include "detail/exceptions.hpp"
#include "detail/value_t.hpp"
namespace
nlohmann
{
template
<
typename
BasicJsonType
>
class
json_pointer
{
// allow basic_json to access private members
NLOHMANN_BASIC_JSON_TPL_DECLARATION
friend
class
basic_json
;
public:
/*!
@brief create JSON pointer
Create a JSON pointer according to the syntax described in
[Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3).
@param[in] s string representing the JSON pointer; if omitted, the empty
string is assumed which references the whole JSON value
@throw parse_error.107 if the given JSON pointer @a s is nonempty and does
not begin with a slash (`/`); see example below
@throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is
not followed by `0` (representing `~`) or `1` (representing `/`); see
example below
@liveexample{The example shows the construction several valid JSON pointers
as well as the exceptional behavior.,json_pointer}
@since version 2.0.0
*/
explicit
json_pointer
(
const
std
::
string
&
s
=
""
)
:
reference_tokens
(
split
(
s
))
{}
/*!
@brief return a string representation of the JSON pointer
@invariant For each JSON pointer `ptr`, it holds:
@code {.cpp}
ptr == json_pointer(ptr.to_string());
@endcode
@return a string representation of the JSON pointer
@liveexample{The example shows the result of `to_string`.,
json_pointer__to_string}
@since version 2.0.0
*/
std
::
string
to_string
()
const
noexcept
{
return
std
::
accumulate
(
reference_tokens
.
begin
(),
reference_tokens
.
end
(),
std
::
string
{},
[](
const
std
::
string
&
a
,
const
std
::
string
&
b
)
{
return
a
+
"/"
+
escape
(
b
);
});
}
/// @copydoc to_string()
operator
std
::
string
()
const
{
return
to_string
();
}
/*!
@param[in] s reference token to be converted into an array index
@return integer representation of @a s
@throw out_of_range.404 if string @a s could not be converted to an integer
*/
static
int
array_index
(
const
std
::
string
&
s
)
{
std
::
size_t
processed_chars
=
0
;
const
int
res
=
std
::
stoi
(
s
,
&
processed_chars
);
// check if the string was completely read
if
(
JSON_UNLIKELY
(
processed_chars
!=
s
.
size
()))
{
JSON_THROW
(
detail
::
out_of_range
::
create
(
404
,
"unresolved reference token '"
+
s
+
"'"
));
}
return
res
;
}
private:
/*!
@brief remove and return last reference pointer
@throw out_of_range.405 if JSON pointer has no parent
*/
std
::
string
pop_back
()
{
if
(
JSON_UNLIKELY
(
is_root
()))
{
JSON_THROW
(
detail
::
out_of_range
::
create
(
405
,
"JSON pointer has no parent"
));
}
auto
last
=
reference_tokens
.
back
();
reference_tokens
.
pop_back
();
return
last
;
}
/// return whether pointer points to the root document
bool
is_root
()
const
{
return
reference_tokens
.
empty
();
}
json_pointer
top
()
const
{
if
(
JSON_UNLIKELY
(
is_root
()))
{
JSON_THROW
(
detail
::
out_of_range
::
create
(
405
,
"JSON pointer has no parent"
));
}
json_pointer
result
=
*
this
;
result
.
reference_tokens
=
{
reference_tokens
[
0
]};
return
result
;
}
/*!
@brief create and return a reference to the pointed to value
@complexity Linear in the number of reference tokens.
@throw parse_error.109 if array index is not a number
@throw type_error.313 if value cannot be unflattened
*/
BasicJsonType
&
get_and_create
(
BasicJsonType
&
j
)
const
{
using
size_type
=
typename
BasicJsonType
::
size_type
;
auto
result
=
&
j
;
// in case no reference tokens exist, return a reference to the JSON value
// j which will be overwritten by a primitive value
for
(
const
auto
&
reference_token
:
reference_tokens
)
{
switch
(
result
->
m_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
result
=
&
result
->
operator
[](
reference_token
);
break
;
}
case
detail
:
:
value_t
::
array
:
{
// create an entry in the array
JSON_TRY
{
result
=
&
result
->
operator
[](
static_cast
<
size_type
>
(
array_index
(
reference_token
)));
}
JSON_CATCH
(
std
::
invalid_argument
&
)
{
JSON_THROW
(
detail
::
parse_error
::
create
(
109
,
0
,
"array index '"
+
reference_token
+
"' is not a number"
));
}
break
;
}
/*
The following code is only reached if there exists a reference
token _and_ the current value is primitive. In this case, we have
an error situation, because primitive values may only occur as
single value; that is, with an empty list of reference tokens.
*/
default:
JSON_THROW
(
detail
::
type_error
::
create
(
313
,
"invalid value to unflatten"
));
}
}
return
*
result
;
}
/*!
@brief return a reference to the pointed to value
@note This version does not throw if a value is not present, but tries to
create nested values instead. For instance, calling this function
with pointer `"/this/that"` on a null value is equivalent to calling
`operator[]("this").operator[]("that")` on that value, effectively
changing the null value to an object.
@param[in] ptr a JSON value
@return reference to the JSON value pointed to by the JSON pointer
@complexity Linear in the length of the JSON pointer.
@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
*/
BasicJsonType
&
get_unchecked
(
BasicJsonType
*
ptr
)
const
{
using
size_type
=
typename
BasicJsonType
::
size_type
;
for
(
const
auto
&
reference_token
:
reference_tokens
)
{
// convert null values to arrays or objects before continuing
if
(
ptr
->
m_type
==
detail
::
value_t
::
null
)
{
// check if reference token is a number
const
bool
nums
=
std
::
all_of
(
reference_token
.
begin
(),
reference_token
.
end
(),
[](
const
char
x
)
{
return
(
x
>=
'0'
and
x
<=
'9'
);
});
// change value to array for numbers or "-" or to object otherwise
*
ptr
=
(
nums
or
reference_token
==
"-"
)
?
detail
::
value_t
::
array
:
detail
::
value_t
::
object
;
}
switch
(
ptr
->
m_type
)
{
case
detail
:
:
value_t
::
object
:
{
// use unchecked object access
ptr
=
&
ptr
->
operator
[](
reference_token
);
break
;
}
case
detail
:
:
value_t
::
array
:
{
// error condition (cf. RFC 6901, Sect. 4)
if
(
JSON_UNLIKELY
(
reference_token
.
size
()
>
1
and
reference_token
[
0
]
==
'0'
))
{
JSON_THROW
(
detail
::
parse_error
::
create
(
106
,
0
,
"array index '"
+
reference_token
+
"' must not begin with '0'"
));
}
if
(
reference_token
==
"-"
)
{
// explicitly treat "-" as index beyond the end
ptr
=
&
ptr
->
operator
[](
ptr
->
m_value
.
array
->
size
());
}
else
{
// convert array index to number; unchecked access
JSON_TRY
{
ptr
=
&
ptr
->
operator
[](
static_cast
<
size_type
>
(
array_index
(
reference_token
)));
}
JSON_CATCH
(
std
::
invalid_argument
&
)
{
JSON_THROW
(
detail
::
parse_error
::
create
(
109
,
0
,
"array index '"
+
reference_token
+
"' is not a number"
));
}
}
break
;
}
default:
JSON_THROW
(
detail
::
out_of_range
::
create
(
404
,
"unresolved reference token '"
+
reference_token
+
"'"
));
}
}
return
*
ptr
;
}
/*!
@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.402 if the array index '-' is used
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
BasicJsonType
&
get_checked
(
BasicJsonType
*
ptr
)
const
{
using
size_type
=
typename
BasicJsonType
::
size_type
;
for
(
const
auto
&
reference_token
:
reference_tokens
)
{
switch
(
ptr
->
m_type
)
{
case
detail
:
:
value_t
::
object
:
{
// note: at performs range check
ptr
=
&
ptr
->
at
(
reference_token
);
break
;
}
case
detail
:
:
value_t
::
array
:
{
if
(
JSON_UNLIKELY
(
reference_token
==
"-"
))
{
// "-" always fails the range check
JSON_THROW
(
detail
::
out_of_range
::
create
(
402
,
"array index '-' ("
+
std
::
to_string
(
ptr
->
m_value
.
array
->
size
())
+
") is out of range"
));
}
// error condition (cf. RFC 6901, Sect. 4)
if
(
JSON_UNLIKELY
(
reference_token
.
size
()
>
1
and
reference_token
[
0
]
==
'0'
))
{
JSON_THROW
(
detail
::
parse_error
::
create
(
106
,
0
,
"array index '"
+
reference_token
+
"' must not begin with '0'"
));
}
// note: at performs range check
JSON_TRY
{
ptr
=
&
ptr
->
at
(
static_cast
<
size_type
>
(
array_index
(
reference_token
)));
}
JSON_CATCH
(
std
::
invalid_argument
&
)
{
JSON_THROW
(
detail
::
parse_error
::
create
(
109
,
0
,
"array index '"
+
reference_token
+
"' is not a number"
));
}
break
;
}
default:
JSON_THROW
(
detail
::
out_of_range
::
create
(
404
,
"unresolved reference token '"
+
reference_token
+
"'"
));
}
}
return
*
ptr
;
}
/*!
@brief return a const reference to the pointed to value
@param[in] ptr a JSON value
@return const reference to the JSON value pointed to by the JSON
pointer
@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.402 if the array index '-' is used
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
const
BasicJsonType
&
get_unchecked
(
const
BasicJsonType
*
ptr
)
const
{
using
size_type
=
typename
BasicJsonType
::
size_type
;
for
(
const
auto
&
reference_token
:
reference_tokens
)
{
switch
(
ptr
->
m_type
)
{
case
detail
:
:
value_t
::
object
:
{
// use unchecked object access
ptr
=
&
ptr
->
operator
[](
reference_token
);
break
;
}
case
detail
:
:
value_t
::
array
:
{
if
(
JSON_UNLIKELY
(
reference_token
==
"-"
))
{
// "-" cannot be used for const access
JSON_THROW
(
detail
::
out_of_range
::
create
(
402
,
"array index '-' ("
+
std
::
to_string
(
ptr
->
m_value
.
array
->
size
())
+
") is out of range"
));
}
// error condition (cf. RFC 6901, Sect. 4)
if
(
JSON_UNLIKELY
(
reference_token
.
size
()
>
1
and
reference_token
[
0
]
==
'0'
))
{
JSON_THROW
(
detail
::
parse_error
::
create
(
106
,
0
,
"array index '"
+
reference_token
+
"' must not begin with '0'"
));
}
// use unchecked array access
JSON_TRY
{
ptr
=
&
ptr
->
operator
[](
static_cast
<
size_type
>
(
array_index
(
reference_token
)));
}
JSON_CATCH
(
std
::
invalid_argument
&
)
{
JSON_THROW
(
detail
::
parse_error
::
create
(
109
,
0
,
"array index '"
+
reference_token
+
"' is not a number"
));
}
break
;
}
default:
JSON_THROW
(
detail
::
out_of_range
::
create
(
404
,
"unresolved reference token '"
+
reference_token
+
"'"
));
}
}
return
*
ptr
;
}
/*!
@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.402 if the array index '-' is used
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
const
BasicJsonType
&
get_checked
(
const
BasicJsonType
*
ptr
)
const
{
using
size_type
=
typename
BasicJsonType
::
size_type
;
for
(
const
auto
&
reference_token
:
reference_tokens
)
{
switch
(
ptr
->
m_type
)
{
case
detail
:
:
value_t
::
object
:
{
// note: at performs range check
ptr
=
&
ptr
->
at
(
reference_token
);
break
;
}
case
detail
:
:
value_t
::
array
:
{
if
(
JSON_UNLIKELY
(
reference_token
==
"-"
))
{
// "-" always fails the range check
JSON_THROW
(
detail
::
out_of_range
::
create
(
402
,
"array index '-' ("
+
std
::
to_string
(
ptr
->
m_value
.
array
->
size
())
+
") is out of range"
));
}
// error condition (cf. RFC 6901, Sect. 4)
if
(
JSON_UNLIKELY
(
reference_token
.
size
()
>
1
and
reference_token
[
0
]
==
'0'
))
{
JSON_THROW
(
detail
::
parse_error
::
create
(
106
,
0
,
"array index '"
+
reference_token
+
"' must not begin with '0'"
));
}
// note: at performs range check
JSON_TRY
{
ptr
=
&
ptr
->
at
(
static_cast
<
size_type
>
(
array_index
(
reference_token
)));
}
JSON_CATCH
(
std
::
invalid_argument
&
)
{
JSON_THROW
(
detail
::
parse_error
::
create
(
109
,
0
,
"array index '"
+
reference_token
+
"' is not a number"
));
}
break
;
}
default:
JSON_THROW
(
detail
::
out_of_range
::
create
(
404
,
"unresolved reference token '"
+
reference_token
+
"'"
));
}
}
return
*
ptr
;
}
/*!
@brief split the string input to reference tokens
@note This function is only called by the json_pointer constructor.
All exceptions below are documented there.
@throw parse_error.107 if the pointer is not empty or begins with '/'
@throw parse_error.108 if character '~' is not followed by '0' or '1'
*/
static
std
::
vector
<
std
::
string
>
split
(
const
std
::
string
&
reference_string
)
{
std
::
vector
<
std
::
string
>
result
;
// special case: empty reference string -> no reference tokens
if
(
reference_string
.
empty
())
{
return
result
;
}
// check if nonempty reference string begins with slash
if
(
JSON_UNLIKELY
(
reference_string
[
0
]
!=
'/'
))
{
JSON_THROW
(
detail
::
parse_error
::
create
(
107
,
1
,
"JSON pointer must be empty or begin with '/' - was: '"
+
reference_string
+
"'"
));
}
// 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
std
::
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
// (will eventually 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
(
std
::
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
(
JSON_UNLIKELY
(
pos
==
reference_token
.
size
()
-
1
or
(
reference_token
[
pos
+
1
]
!=
'0'
and
reference_token
[
pos
+
1
]
!=
'1'
)))
{
JSON_THROW
(
detail
::
parse_error
::
create
(
108
,
0
,
"escape character '~' must be followed with '0' or '1'"
));
}
}
// finally, store the reference token
unescape
(
reference_token
);
result
.
push_back
(
reference_token
);
}
return
result
;
}
/*!
@brief replace all occurrences of a substring by another string
@param[in,out] s the string to manipulate; changed so that all
occurrences of @a f are replaced with @a t
@param[in] f the substring to replace with @a t
@param[in] t the string to replace @a f
@pre The search string @a f must not be empty. **This precondition is
enforced with an assertion.**
@since version 2.0.0
*/
static
void
replace_substring
(
std
::
string
&
s
,
const
std
::
string
&
f
,
const
std
::
string
&
t
)
{
assert
(
not
f
.
empty
());
for
(
auto
pos
=
s
.
find
(
f
);
// find first occurrence of f
pos
!=
std
::
string
::
npos
;
// make sure f was found
s
.
replace
(
pos
,
f
.
size
(),
t
),
// replace with t, and
pos
=
s
.
find
(
f
,
pos
+
t
.
size
()))
// find next occurrence of f
{}
}
/// escape "~"" to "~0" and "/" to "~1"
static
std
::
string
escape
(
std
::
string
s
)
{
replace_substring
(
s
,
"~"
,
"~0"
);
replace_substring
(
s
,
"/"
,
"~1"
);
return
s
;
}
/// unescape "~1" to tilde and "~0" to slash (order is important!)
static
void
unescape
(
std
::
string
&
s
)
{
replace_substring
(
s
,
"~1"
,
"/"
);
replace_substring
(
s
,
"~0"
,
"~"
);
}
/*!
@param[in] reference_string the reference string to the current value
@param[in] value the value to consider
@param[in,out] result the result object to insert values to
@note Empty objects or arrays are flattened to `null`.
*/
static
void
flatten
(
const
std
::
string
&
reference_string
,
const
BasicJsonType
&
value
,
BasicJsonType
&
result
)
{
switch
(
value
.
m_type
)
{
case
detail
:
:
value_t
::
array
:
{
if
(
value
.
m_value
.
array
->
empty
())
{
// flatten empty array as null
result
[
reference_string
]
=
nullptr
;
}
else
{
// iterate array and use index as reference string
for
(
std
::
size_t
i
=
0
;
i
<
value
.
m_value
.
array
->
size
();
++
i
)
{
flatten
(
reference_string
+
"/"
+
std
::
to_string
(
i
),
value
.
m_value
.
array
->
operator
[](
i
),
result
);
}
}
break
;
}
case
detail
:
:
value_t
::
object
:
{
if
(
value
.
m_value
.
object
->
empty
())
{
// flatten empty object as null
result
[
reference_string
]
=
nullptr
;
}
else
{
// iterate object and use keys as reference string
for
(
const
auto
&
element
:
*
value
.
m_value
.
object
)
{
flatten
(
reference_string
+
"/"
+
escape
(
element
.
first
),
element
.
second
,
result
);
}
}
break
;
}
default:
{
// add primitive value with its reference string
result
[
reference_string
]
=
value
;
break
;
}
}
}
/*!
@param[in] value flattened JSON
@return unflattened JSON
@throw parse_error.109 if array index is not a number
@throw type_error.314 if value is not an object
@throw type_error.315 if object values are not primitive
@throw type_error.313 if value cannot be unflattened
*/
static
BasicJsonType
unflatten
(
const
BasicJsonType
&
value
)
{
if
(
JSON_UNLIKELY
(
not
value
.
is_object
()))
{
JSON_THROW
(
detail
::
type_error
::
create
(
314
,
"only objects can be unflattened"
));
}
BasicJsonType
result
;
// iterate the JSON object values
for
(
const
auto
&
element
:
*
value
.
m_value
.
object
)
{
if
(
JSON_UNLIKELY
(
not
element
.
second
.
is_primitive
()))
{
JSON_THROW
(
detail
::
type_error
::
create
(
315
,
"values in object must be primitive"
));
}
// assign value to reference pointed to by JSON pointer; Note that if
// the JSON pointer is "" (i.e., points to the whole value), function
// get_and_create returns a reference to result itself. An assignment
// will then create a primitive value.
json_pointer
(
element
.
first
).
get_and_create
(
result
)
=
element
.
second
;
}
return
result
;
}
friend
bool
operator
==
(
json_pointer
const
&
lhs
,
json_pointer
const
&
rhs
)
noexcept
{
return
(
lhs
.
reference_tokens
==
rhs
.
reference_tokens
);
}
friend
bool
operator
!=
(
json_pointer
const
&
lhs
,
json_pointer
const
&
rhs
)
noexcept
{
return
not
(
lhs
==
rhs
);
}
/// the reference tokens
std
::
vector
<
std
::
string
>
reference_tokens
;
};
}
This diff is collapsed.
Click to expand it.
develop/detail/output/serializer.hpp
View file @
1483d39c
...
...
@@ -9,11 +9,14 @@
#include <cstddef> // size_t, ptrdiff_t
#include <cstdint> // uint8_t
#include <cstdio> // snprintf
#include <iomanip> // setfill
#include <iterator> // next
#include <limits> // numeric_limits
#include <string> // string
#include <sstream> // stringstream
#include <type_traits> // is_same
#include "detail/exceptions.hpp"
#include "detail/conversions/to_chars.hpp"
#include "detail/macro_scope.hpp"
#include "detail/meta.hpp"
...
...
This diff is collapsed.
Click to expand it.
develop/json.hpp
View file @
1483d39c
...
...
@@ -61,6 +61,7 @@ SOFTWARE.
#include "detail/output/binary_writer.hpp"
#include "detail/output/serializer.hpp"
#include "detail/json_ref.hpp"
#include "detail/json_pointer.hpp"
#include "adl_serializer.hpp"
/*!
...
...
@@ -70,335 +71,6 @@ SOFTWARE.
*/
namespace
nlohmann
{
class
json_pointer
{
/// allow basic_json to access private members
NLOHMANN_BASIC_JSON_TPL_DECLARATION
friend
class
basic_json
;
public:
/*!
@brief create JSON pointer
Create a JSON pointer according to the syntax described in
[Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3).
@param[in] s string representing the JSON pointer; if omitted, the empty
string is assumed which references the whole JSON value
@throw parse_error.107 if the given JSON pointer @a s is nonempty and
does not begin with a slash (`/`); see example below
@throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s
is not followed by `0` (representing `~`) or `1` (representing `/`);
see example below
@liveexample{The example shows the construction several valid JSON
pointers as well as the exceptional behavior.,json_pointer}
@since version 2.0.0
*/
explicit
json_pointer
(
const
std
::
string
&
s
=
""
)
:
reference_tokens
(
split
(
s
))
{}
/*!
@brief return a string representation of the JSON pointer
@invariant For each JSON pointer `ptr`, it holds:
@code {.cpp}
ptr == json_pointer(ptr.to_string());
@endcode
@return a string representation of the JSON pointer
@liveexample{The example shows the result of `to_string`.,
json_pointer__to_string}
@since version 2.0.0
*/
std
::
string
to_string
()
const
noexcept
{
return
std
::
accumulate
(
reference_tokens
.
begin
(),
reference_tokens
.
end
(),
std
::
string
{},
[](
const
std
::
string
&
a
,
const
std
::
string
&
b
)
{
return
a
+
"/"
+
escape
(
b
);
});
}
/// @copydoc to_string()
operator
std
::
string
()
const
{
return
to_string
();
}
/*!
@param[in] s reference token to be converted into an array index
@return integer representation of @a s
@throw out_of_range.404 if string @a s could not be converted to an integer
*/
static
int
array_index
(
const
std
::
string
&
s
)
{
std
::
size_t
processed_chars
=
0
;
const
int
res
=
std
::
stoi
(
s
,
&
processed_chars
);
// check if the string was completely read
if
(
JSON_UNLIKELY
(
processed_chars
!=
s
.
size
()))
{
JSON_THROW
(
detail
::
out_of_range
::
create
(
404
,
"unresolved reference token '"
+
s
+
"'"
));
}
return
res
;
}
private:
/*!
@brief remove and return last reference pointer
@throw out_of_range.405 if JSON pointer has no parent
*/
std
::
string
pop_back
()
{
if
(
JSON_UNLIKELY
(
is_root
()))
{
JSON_THROW
(
detail
::
out_of_range
::
create
(
405
,
"JSON pointer has no parent"
));
}
auto
last
=
reference_tokens
.
back
();
reference_tokens
.
pop_back
();
return
last
;
}
/// return whether pointer points to the root document
bool
is_root
()
const
{
return
reference_tokens
.
empty
();
}
json_pointer
top
()
const
{
if
(
JSON_UNLIKELY
(
is_root
()))
{
JSON_THROW
(
detail
::
out_of_range
::
create
(
405
,
"JSON pointer has no parent"
));
}
json_pointer
result
=
*
this
;
result
.
reference_tokens
=
{
reference_tokens
[
0
]};
return
result
;
}
/*!
@brief create and return a reference to the pointed to value
@complexity Linear in the number of reference tokens.
@throw parse_error.109 if array index is not a number
@throw type_error.313 if value cannot be unflattened
*/
NLOHMANN_BASIC_JSON_TPL_DECLARATION
NLOHMANN_BASIC_JSON_TPL
&
get_and_create
(
NLOHMANN_BASIC_JSON_TPL
&
j
)
const
;
/*!
@brief return a reference to the pointed to value
@note This version does not throw if a value is not present, but tries to
create nested values instead. For instance, calling this function
with pointer `"/this/that"` on a null value is equivalent to calling
`operator[]("this").operator[]("that")` on that value, effectively
changing the null value to an object.
@param[in] ptr a JSON value
@return reference to the JSON value pointed to by the JSON pointer
@complexity Linear in the length of the JSON pointer.
@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
*/
NLOHMANN_BASIC_JSON_TPL_DECLARATION
NLOHMANN_BASIC_JSON_TPL
&
get_unchecked
(
NLOHMANN_BASIC_JSON_TPL
*
ptr
)
const
;
/*!
@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.402 if the array index '-' is used
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
NLOHMANN_BASIC_JSON_TPL_DECLARATION
NLOHMANN_BASIC_JSON_TPL
&
get_checked
(
NLOHMANN_BASIC_JSON_TPL
*
ptr
)
const
;
/*!
@brief return a const reference to the pointed to value
@param[in] ptr a JSON value
@return const reference to the JSON value pointed to by the JSON
pointer
@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.402 if the array index '-' is used
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
NLOHMANN_BASIC_JSON_TPL_DECLARATION
const
NLOHMANN_BASIC_JSON_TPL
&
get_unchecked
(
const
NLOHMANN_BASIC_JSON_TPL
*
ptr
)
const
;
/*!
@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.402 if the array index '-' is used
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
NLOHMANN_BASIC_JSON_TPL_DECLARATION
const
NLOHMANN_BASIC_JSON_TPL
&
get_checked
(
const
NLOHMANN_BASIC_JSON_TPL
*
ptr
)
const
;
/*!
@brief split the string input to reference tokens
@note This function is only called by the json_pointer constructor.
All exceptions below are documented there.
@throw parse_error.107 if the pointer is not empty or begins with '/'
@throw parse_error.108 if character '~' is not followed by '0' or '1'
*/
static
std
::
vector
<
std
::
string
>
split
(
const
std
::
string
&
reference_string
)
{
std
::
vector
<
std
::
string
>
result
;
// special case: empty reference string -> no reference tokens
if
(
reference_string
.
empty
())
{
return
result
;
}
// check if nonempty reference string begins with slash
if
(
JSON_UNLIKELY
(
reference_string
[
0
]
!=
'/'
))
{
JSON_THROW
(
detail
::
parse_error
::
create
(
107
,
1
,
"JSON pointer must be empty or begin with '/' - was: '"
+
reference_string
+
"'"
));
}
// 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
std
::
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
// (will eventually 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
(
std
::
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
(
JSON_UNLIKELY
(
pos
==
reference_token
.
size
()
-
1
or
(
reference_token
[
pos
+
1
]
!=
'0'
and
reference_token
[
pos
+
1
]
!=
'1'
)))
{
JSON_THROW
(
detail
::
parse_error
::
create
(
108
,
0
,
"escape character '~' must be followed with '0' or '1'"
));
}
}
// finally, store the reference token
unescape
(
reference_token
);
result
.
push_back
(
reference_token
);
}
return
result
;
}
/*!
@brief replace all occurrences of a substring by another string
@param[in,out] s the string to manipulate; changed so that all
occurrences of @a f are replaced with @a t
@param[in] f the substring to replace with @a t
@param[in] t the string to replace @a f
@pre The search string @a f must not be empty. **This precondition is
enforced with an assertion.**
@since version 2.0.0
*/
static
void
replace_substring
(
std
::
string
&
s
,
const
std
::
string
&
f
,
const
std
::
string
&
t
)
{
assert
(
not
f
.
empty
());
for
(
auto
pos
=
s
.
find
(
f
);
// find first occurrence of f
pos
!=
std
::
string
::
npos
;
// make sure f was found
s
.
replace
(
pos
,
f
.
size
(),
t
),
// replace with t, and
pos
=
s
.
find
(
f
,
pos
+
t
.
size
()))
// find next occurrence of f
{}
}
/// escape "~"" to "~0" and "/" to "~1"
static
std
::
string
escape
(
std
::
string
s
)
{
replace_substring
(
s
,
"~"
,
"~0"
);
replace_substring
(
s
,
"/"
,
"~1"
);
return
s
;
}
/// unescape "~1" to tilde and "~0" to slash (order is important!)
static
void
unescape
(
std
::
string
&
s
)
{
replace_substring
(
s
,
"~1"
,
"/"
);
replace_substring
(
s
,
"~0"
,
"~"
);
}
/*!
@param[in] reference_string the reference string to the current value
@param[in] value the value to consider
@param[in,out] result the result object to insert values to
@note Empty objects or arrays are flattened to `null`.
*/
NLOHMANN_BASIC_JSON_TPL_DECLARATION
static
void
flatten
(
const
std
::
string
&
reference_string
,
const
NLOHMANN_BASIC_JSON_TPL
&
value
,
NLOHMANN_BASIC_JSON_TPL
&
result
);
/*!
@param[in] value flattened JSON
@return unflattened JSON
@throw parse_error.109 if array index is not a number
@throw type_error.314 if value is not an object
@throw type_error.315 if object values are not primitive
@throw type_error.313 if value cannot be unflattened
*/
NLOHMANN_BASIC_JSON_TPL_DECLARATION
static
NLOHMANN_BASIC_JSON_TPL
unflatten
(
const
NLOHMANN_BASIC_JSON_TPL
&
value
);
friend
bool
operator
==
(
json_pointer
const
&
lhs
,
json_pointer
const
&
rhs
)
noexcept
;
friend
bool
operator
!=
(
json_pointer
const
&
lhs
,
json_pointer
const
&
rhs
)
noexcept
;
/// the reference tokens
std
::
vector
<
std
::
string
>
reference_tokens
;
};
/*!
@brief a class to store JSON values
...
...
@@ -486,7 +158,7 @@ class basic_json
{
private:
template
<
detail
::
value_t
>
friend
struct
detail
::
external_constructor
;
friend
::
nlohmann
::
json_pointer
;
friend
::
nlohmann
::
json_pointer
<
basic_json
>
;
friend
::
nlohmann
::
detail
::
parser
<
basic_json
>
;
friend
::
nlohmann
::
detail
::
serializer
<
basic_json
>
;
template
<
typename
BasicJsonType
>
...
...
@@ -523,7 +195,7 @@ class basic_json
public:
using
value_t
=
detail
::
value_t
;
/// @copydoc nlohmann::json_pointer
using
json_pointer
=
::
nlohmann
::
json_pointer
;
using
json_pointer
=
::
nlohmann
::
json_pointer
<
basic_json
>
;
template
<
typename
T
,
typename
SFINAE
>
using
json_serializer
=
JSONSerializer
<
T
,
SFINAE
>
;
/// helper type for initializer lists of basic_json values
...
...
@@ -7789,400 +7461,6 @@ class basic_json
/// @}
};
//////////////////
// json_pointer //
//////////////////
NLOHMANN_BASIC_JSON_TPL_DECLARATION
NLOHMANN_BASIC_JSON_TPL
&
json_pointer
::
get_and_create
(
NLOHMANN_BASIC_JSON_TPL
&
j
)
const
{
using
size_type
=
typename
NLOHMANN_BASIC_JSON_TPL
::
size_type
;
auto
result
=
&
j
;
// in case no reference tokens exist, return a reference to the JSON value
// j which will be overwritten by a primitive value
for
(
const
auto
&
reference_token
:
reference_tokens
)
{
switch
(
result
->
m_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
result
=
&
result
->
operator
[](
reference_token
);
break
;
}
case
detail
:
:
value_t
::
array
:
{
// create an entry in the array
JSON_TRY
{
result
=
&
result
->
operator
[](
static_cast
<
size_type
>
(
array_index
(
reference_token
)));
}
JSON_CATCH
(
std
::
invalid_argument
&
)
{
JSON_THROW
(
detail
::
parse_error
::
create
(
109
,
0
,
"array index '"
+
reference_token
+
"' is not a number"
));
}
break
;
}
/*
The following code is only reached if there exists a reference
token _and_ the current value is primitive. In this case, we have
an error situation, because primitive values may only occur as
single value; that is, with an empty list of reference tokens.
*/
default:
JSON_THROW
(
detail
::
type_error
::
create
(
313
,
"invalid value to unflatten"
));
}
}
return
*
result
;
}
NLOHMANN_BASIC_JSON_TPL_DECLARATION
NLOHMANN_BASIC_JSON_TPL
&
json_pointer
::
get_unchecked
(
NLOHMANN_BASIC_JSON_TPL
*
ptr
)
const
{
using
size_type
=
typename
NLOHMANN_BASIC_JSON_TPL
::
size_type
;
for
(
const
auto
&
reference_token
:
reference_tokens
)
{
// convert null values to arrays or objects before continuing
if
(
ptr
->
m_type
==
detail
::
value_t
::
null
)
{
// check if reference token is a number
const
bool
nums
=
std
::
all_of
(
reference_token
.
begin
(),
reference_token
.
end
(),
[](
const
char
x
)
{
return
(
x
>=
'0'
and
x
<=
'9'
);
});
// change value to array for numbers or "-" or to object otherwise
*
ptr
=
(
nums
or
reference_token
==
"-"
)
?
detail
::
value_t
::
array
:
detail
::
value_t
::
object
;
}
switch
(
ptr
->
m_type
)
{
case
detail
:
:
value_t
::
object
:
{
// use unchecked object access
ptr
=
&
ptr
->
operator
[](
reference_token
);
break
;
}
case
detail
:
:
value_t
::
array
:
{
// error condition (cf. RFC 6901, Sect. 4)
if
(
JSON_UNLIKELY
(
reference_token
.
size
()
>
1
and
reference_token
[
0
]
==
'0'
))
{
JSON_THROW
(
detail
::
parse_error
::
create
(
106
,
0
,
"array index '"
+
reference_token
+
"' must not begin with '0'"
));
}
if
(
reference_token
==
"-"
)
{
// explicitly treat "-" as index beyond the end
ptr
=
&
ptr
->
operator
[](
ptr
->
m_value
.
array
->
size
());
}
else
{
// convert array index to number; unchecked access
JSON_TRY
{
ptr
=
&
ptr
->
operator
[](
static_cast
<
size_type
>
(
array_index
(
reference_token
)));
}
JSON_CATCH
(
std
::
invalid_argument
&
)
{
JSON_THROW
(
detail
::
parse_error
::
create
(
109
,
0
,
"array index '"
+
reference_token
+
"' is not a number"
));
}
}
break
;
}
default:
JSON_THROW
(
detail
::
out_of_range
::
create
(
404
,
"unresolved reference token '"
+
reference_token
+
"'"
));
}
}
return
*
ptr
;
}
NLOHMANN_BASIC_JSON_TPL_DECLARATION
NLOHMANN_BASIC_JSON_TPL
&
json_pointer
::
get_checked
(
NLOHMANN_BASIC_JSON_TPL
*
ptr
)
const
{
using
size_type
=
typename
NLOHMANN_BASIC_JSON_TPL
::
size_type
;
for
(
const
auto
&
reference_token
:
reference_tokens
)
{
switch
(
ptr
->
m_type
)
{
case
detail
:
:
value_t
::
object
:
{
// note: at performs range check
ptr
=
&
ptr
->
at
(
reference_token
);
break
;
}
case
detail
:
:
value_t
::
array
:
{
if
(
JSON_UNLIKELY
(
reference_token
==
"-"
))
{
// "-" always fails the range check
JSON_THROW
(
detail
::
out_of_range
::
create
(
402
,
"array index '-' ("
+
std
::
to_string
(
ptr
->
m_value
.
array
->
size
())
+
") is out of range"
));
}
// error condition (cf. RFC 6901, Sect. 4)
if
(
JSON_UNLIKELY
(
reference_token
.
size
()
>
1
and
reference_token
[
0
]
==
'0'
))
{
JSON_THROW
(
detail
::
parse_error
::
create
(
106
,
0
,
"array index '"
+
reference_token
+
"' must not begin with '0'"
));
}
// note: at performs range check
JSON_TRY
{
ptr
=
&
ptr
->
at
(
static_cast
<
size_type
>
(
array_index
(
reference_token
)));
}
JSON_CATCH
(
std
::
invalid_argument
&
)
{
JSON_THROW
(
detail
::
parse_error
::
create
(
109
,
0
,
"array index '"
+
reference_token
+
"' is not a number"
));
}
break
;
}
default:
JSON_THROW
(
detail
::
out_of_range
::
create
(
404
,
"unresolved reference token '"
+
reference_token
+
"'"
));
}
}
return
*
ptr
;
}
NLOHMANN_BASIC_JSON_TPL_DECLARATION
const
NLOHMANN_BASIC_JSON_TPL
&
json_pointer
::
get_unchecked
(
const
NLOHMANN_BASIC_JSON_TPL
*
ptr
)
const
{
using
size_type
=
typename
NLOHMANN_BASIC_JSON_TPL
::
size_type
;
for
(
const
auto
&
reference_token
:
reference_tokens
)
{
switch
(
ptr
->
m_type
)
{
case
detail
:
:
value_t
::
object
:
{
// use unchecked object access
ptr
=
&
ptr
->
operator
[](
reference_token
);
break
;
}
case
detail
:
:
value_t
::
array
:
{
if
(
JSON_UNLIKELY
(
reference_token
==
"-"
))
{
// "-" cannot be used for const access
JSON_THROW
(
detail
::
out_of_range
::
create
(
402
,
"array index '-' ("
+
std
::
to_string
(
ptr
->
m_value
.
array
->
size
())
+
") is out of range"
));
}
// error condition (cf. RFC 6901, Sect. 4)
if
(
JSON_UNLIKELY
(
reference_token
.
size
()
>
1
and
reference_token
[
0
]
==
'0'
))
{
JSON_THROW
(
detail
::
parse_error
::
create
(
106
,
0
,
"array index '"
+
reference_token
+
"' must not begin with '0'"
));
}
// use unchecked array access
JSON_TRY
{
ptr
=
&
ptr
->
operator
[](
static_cast
<
size_type
>
(
array_index
(
reference_token
)));
}
JSON_CATCH
(
std
::
invalid_argument
&
)
{
JSON_THROW
(
detail
::
parse_error
::
create
(
109
,
0
,
"array index '"
+
reference_token
+
"' is not a number"
));
}
break
;
}
default:
JSON_THROW
(
detail
::
out_of_range
::
create
(
404
,
"unresolved reference token '"
+
reference_token
+
"'"
));
}
}
return
*
ptr
;
}
NLOHMANN_BASIC_JSON_TPL_DECLARATION
const
NLOHMANN_BASIC_JSON_TPL
&
json_pointer
::
get_checked
(
const
NLOHMANN_BASIC_JSON_TPL
*
ptr
)
const
{
using
size_type
=
typename
NLOHMANN_BASIC_JSON_TPL
::
size_type
;
for
(
const
auto
&
reference_token
:
reference_tokens
)
{
switch
(
ptr
->
m_type
)
{
case
detail
:
:
value_t
::
object
:
{
// note: at performs range check
ptr
=
&
ptr
->
at
(
reference_token
);
break
;
}
case
detail
:
:
value_t
::
array
:
{
if
(
JSON_UNLIKELY
(
reference_token
==
"-"
))
{
// "-" always fails the range check
JSON_THROW
(
detail
::
out_of_range
::
create
(
402
,
"array index '-' ("
+
std
::
to_string
(
ptr
->
m_value
.
array
->
size
())
+
") is out of range"
));
}
// error condition (cf. RFC 6901, Sect. 4)
if
(
JSON_UNLIKELY
(
reference_token
.
size
()
>
1
and
reference_token
[
0
]
==
'0'
))
{
JSON_THROW
(
detail
::
parse_error
::
create
(
106
,
0
,
"array index '"
+
reference_token
+
"' must not begin with '0'"
));
}
// note: at performs range check
JSON_TRY
{
ptr
=
&
ptr
->
at
(
static_cast
<
size_type
>
(
array_index
(
reference_token
)));
}
JSON_CATCH
(
std
::
invalid_argument
&
)
{
JSON_THROW
(
detail
::
parse_error
::
create
(
109
,
0
,
"array index '"
+
reference_token
+
"' is not a number"
));
}
break
;
}
default:
JSON_THROW
(
detail
::
out_of_range
::
create
(
404
,
"unresolved reference token '"
+
reference_token
+
"'"
));
}
}
return
*
ptr
;
}
NLOHMANN_BASIC_JSON_TPL_DECLARATION
void
json_pointer
::
flatten
(
const
std
::
string
&
reference_string
,
const
NLOHMANN_BASIC_JSON_TPL
&
value
,
NLOHMANN_BASIC_JSON_TPL
&
result
)
{
switch
(
value
.
m_type
)
{
case
detail
:
:
value_t
::
array
:
{
if
(
value
.
m_value
.
array
->
empty
())
{
// flatten empty array as null
result
[
reference_string
]
=
nullptr
;
}
else
{
// iterate array and use index as reference string
for
(
std
::
size_t
i
=
0
;
i
<
value
.
m_value
.
array
->
size
();
++
i
)
{
flatten
(
reference_string
+
"/"
+
std
::
to_string
(
i
),
value
.
m_value
.
array
->
operator
[](
i
),
result
);
}
}
break
;
}
case
detail
:
:
value_t
::
object
:
{
if
(
value
.
m_value
.
object
->
empty
())
{
// flatten empty object as null
result
[
reference_string
]
=
nullptr
;
}
else
{
// iterate object and use keys as reference string
for
(
const
auto
&
element
:
*
value
.
m_value
.
object
)
{
flatten
(
reference_string
+
"/"
+
escape
(
element
.
first
),
element
.
second
,
result
);
}
}
break
;
}
default:
{
// add primitive value with its reference string
result
[
reference_string
]
=
value
;
break
;
}
}
}
NLOHMANN_BASIC_JSON_TPL_DECLARATION
NLOHMANN_BASIC_JSON_TPL
json_pointer
::
unflatten
(
const
NLOHMANN_BASIC_JSON_TPL
&
value
)
{
if
(
JSON_UNLIKELY
(
not
value
.
is_object
()))
{
JSON_THROW
(
detail
::
type_error
::
create
(
314
,
"only objects can be unflattened"
));
}
NLOHMANN_BASIC_JSON_TPL
result
;
// iterate the JSON object values
for
(
const
auto
&
element
:
*
value
.
m_value
.
object
)
{
if
(
JSON_UNLIKELY
(
not
element
.
second
.
is_primitive
()))
{
JSON_THROW
(
detail
::
type_error
::
create
(
315
,
"values in object must be primitive"
));
}
// assign value to reference pointed to by JSON pointer; Note that if
// the JSON pointer is "" (i.e., points to the whole value), function
// get_and_create returns a reference to result itself. An assignment
// will then create a primitive value.
json_pointer
(
element
.
first
).
get_and_create
(
result
)
=
element
.
second
;
}
return
result
;
}
inline
bool
operator
==
(
json_pointer
const
&
lhs
,
json_pointer
const
&
rhs
)
noexcept
{
return
(
lhs
.
reference_tokens
==
rhs
.
reference_tokens
);
}
inline
bool
operator
!=
(
json_pointer
const
&
lhs
,
json_pointer
const
&
rhs
)
noexcept
{
return
not
(
lhs
==
rhs
);
}
}
// namespace nlohmann
///////////////////////
...
...
This diff is collapsed.
Click to expand it.
develop/json_fwd.hpp
View file @
1483d39c
...
...
@@ -47,6 +47,7 @@ within a JSON document. It can be used with functions `at` and
@since version 2.0.0
*/
template
<
typename
BasicJsonType
>
class
json_pointer
;
/*!
...
...
This diff is collapsed.
Click to expand it.
src/json.hpp
View file @
1483d39c
...
...
@@ -91,6 +91,7 @@ within a JSON document. It can be used with functions `at` and
@since version 2.0.0
*/
template<typename BasicJsonType>
class json_pointer;
/*!
...
...
@@ -4811,6 +4812,8 @@ class output_adapter
#include <string> // char_traits, string
#include <utility> // make_pair, move
// #include "detail/input/input_adapters.hpp"
// #include "detail/exceptions.hpp"
// #include "detail/macro_scope.hpp"
...
...
@@ -7082,11 +7085,15 @@ class binary_writer
#include <cstddef> // size_t, ptrdiff_t
#include <cstdint> // uint8_t
#include <cstdio> // snprintf
#include <iomanip> // setfill
#include <iterator> // next
#include <limits> // numeric_limits
#include <string> // string
#include <sstream> // stringstream
#include <type_traits> // is_same
// #include "detail/exceptions.hpp"
// #include "detail/conversions/to_chars.hpp"
...
...
@@ -8866,66 +8873,27 @@ class json_ref
}
}
// #include "adl_serializer.hpp"
#include <utility>
// #include "detail/conversions/from_json.hpp"
// #include "detail/conversions/to_json.hpp"
namespace nlohmann
{
template<typename, typename>
struct adl_serializer
{
/*!
@brief convert a JSON value to any value type
// #include "detail/json_pointer.hpp"
This function is usually called by the `get()` function of the
@ref basic_json class (either explicit or via conversion operators).
@param[in] j JSON value to read from
@param[in,out] val value to write to
*/
template<typename BasicJsonType, typename ValueType>
static void from_json(BasicJsonType&& j, ValueType& val) noexcept(
noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))
{
::nlohmann::from_json(std::forward<BasicJsonType>(j), val);
}
#include <cassert> // assert
#include <numeric> // accumulate
#include <string> // string
#include <vector> // vector
/*!
@brief convert any value type to a JSON value
// #include "detail/macro_scope.hpp"
This function is usually called by the constructors of the @ref basic_json
class.
// #include "detail/exceptions.hpp"
@param[in,out] j JSON value to write to
@param[in] val value to read from
*/
template<typename BasicJsonType, typename ValueType>
static void to_json(BasicJsonType& j, ValueType&& val) noexcept(
noexcept(::nlohmann::to_json(j, std::forward<ValueType>(val))))
{
::nlohmann::to_json(j, std::forward<ValueType>(val));
}
};
}
// #include "detail/value_t.hpp"
/*!
@brief namespace for Niels Lohmann
@see https://github.com/nlohmann
@since version 1.0.0
*/
namespace nlohmann
{
template<typename BasicJsonType>
class json_pointer
{
//
/
allow basic_json to access private members
// allow basic_json to access private members
NLOHMANN_BASIC_JSON_TPL_DECLARATION
friend class basic_json;
...
...
@@ -8939,19 +8907,21 @@ class json_pointer
@param[in] s string representing the JSON pointer; if omitted, the empty
string is assumed which references the whole JSON value
@throw parse_error.107 if the given JSON pointer @a s is nonempty and
does
not begin with a slash (`/`); see example below
@throw parse_error.107 if the given JSON pointer @a s is nonempty and
does
not begin with a slash (`/`); see example below
@throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s
is not followed by `0` (representing `~`) or `1` (representing `/`);
see
example below
@throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s
is
not followed by `0` (representing `~`) or `1` (representing `/`); see
example below
@liveexample{The example shows the construction several valid JSON
pointers
as well as the exceptional behavior.,json_pointer}
@liveexample{The example shows the construction several valid JSON
pointers
as well as the exceptional behavior.,json_pointer}
@since version 2.0.0
*/
explicit json_pointer(const std::string& s = "") : reference_tokens(split(s)) {}
explicit json_pointer(const std::string& s = "")
: reference_tokens(split(s))
{}
/*!
@brief return a string representation of the JSON pointer
...
...
@@ -9048,8 +9018,66 @@ class json_pointer
@throw parse_error.109 if array index is not a number
@throw type_error.313 if value cannot be unflattened
*/
NLOHMANN_BASIC_JSON_TPL_DECLARATION
NLOHMANN_BASIC_JSON_TPL& get_and_create(NLOHMANN_BASIC_JSON_TPL& j) const;
BasicJsonType& get_and_create(BasicJsonType& j) const
{
using size_type = typename BasicJsonType::size_type;
auto result = &j;
// in case no reference tokens exist, return a reference to the JSON value
// j which will be overwritten by a primitive value
for (const auto& reference_token : reference_tokens)
{
switch (result->m_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
result = &result->operator[](reference_token);
break;
}
case detail::value_t::array:
{
// create an entry in the array
JSON_TRY
{
result = &result->operator[](static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
}
break;
}
/*
The following code is only reached if there exists a reference
token _and_ the current value is primitive. In this case, we have
an error situation, because primitive values may only occur as
single value; that is, with an empty list of reference tokens.
*/
default:
JSON_THROW(detail::type_error::create(313, "invalid value to unflatten"));
}
}
return *result;
}
/*!
@brief return a reference to the pointed to value
...
...
@@ -9070,8 +9098,75 @@ class json_pointer
@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
*/
NLOHMANN_BASIC_JSON_TPL_DECLARATION
NLOHMANN_BASIC_JSON_TPL& get_unchecked(NLOHMANN_BASIC_JSON_TPL* ptr) const;
BasicJsonType& get_unchecked(BasicJsonType* ptr) const
{
using size_type = typename BasicJsonType::size_type;
for (const auto& reference_token : reference_tokens)
{
// convert null values to arrays or objects before continuing
if (ptr->m_type == detail::value_t::null)
{
// check if reference token is a number
const bool nums =
std::all_of(reference_token.begin(), reference_token.end(),
[](const char x)
{
return (x >= '0' and x <= '9');
});
// change value to array for numbers or "-" or to object otherwise
*ptr = (nums or reference_token == "-")
? detail::value_t::array
: detail::value_t::object;
}
switch (ptr->m_type)
{
case detail::value_t::object:
{
// use unchecked object access
ptr = &ptr->operator[](reference_token);
break;
}
case detail::value_t::array:
{
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
{
JSON_THROW(detail::parse_error::create(106, 0,
"array index '" + reference_token +
"' must not begin with '0'"));
}
if (reference_token == "-")
{
// explicitly treat "-" as index beyond the end
ptr = &ptr->operator[](ptr->m_value.array->size());
}
else
{
// convert array index to number; unchecked access
JSON_TRY
{
ptr = &ptr->operator[](
static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
}
}
break;
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
}
}
return *ptr;
}
/*!
@throw parse_error.106 if an array index begins with '0'
...
...
@@ -9079,8 +9174,57 @@ class json_pointer
@throw out_of_range.402 if the array index '-' is used
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
NLOHMANN_BASIC_JSON_TPL_DECLARATION
NLOHMANN_BASIC_JSON_TPL& get_checked(NLOHMANN_BASIC_JSON_TPL* ptr) const;
BasicJsonType& get_checked(BasicJsonType* ptr) const
{
using size_type = typename BasicJsonType::size_type;
for (const auto& reference_token : reference_tokens)
{
switch (ptr->m_type)
{
case detail::value_t::object:
{
// note: at performs range check
ptr = &ptr->at(reference_token);
break;
}
case detail::value_t::array:
{
if (JSON_UNLIKELY(reference_token == "-"))
{
// "-" always fails the range check
JSON_THROW(detail::out_of_range::create(402,
"array index '-' (" + std::to_string(ptr->m_value.array->size()) +
") is out of range"));
}
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
{
JSON_THROW(detail::parse_error::create(106, 0,
"array index '" + reference_token +
"' must not begin with '0'"));
}
// note: at performs range check
JSON_TRY
{
ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
}
break;
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
}
}
return *ptr;
}
/*!
@brief return a const reference to the pointed to value
...
...
@@ -9095,17 +9239,116 @@ class json_pointer
@throw out_of_range.402 if the array index '-' is used
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
NLOHMANN_BASIC_JSON_TPL_DECLARATION
const NLOHMANN_BASIC_JSON_TPL& get_unchecked(const NLOHMANN_BASIC_JSON_TPL* ptr) const;
const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const
{
using size_type = typename BasicJsonType::size_type;
for (const auto& reference_token : reference_tokens)
{
switch (ptr->m_type)
{
case detail::value_t::object:
{
// use unchecked object access
ptr = &ptr->operator[](reference_token);
break;
}
/*!
@throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number
case detail::value_t::array:
{
if (JSON_UNLIKELY(reference_token == "-"))
{
// "-" cannot be used for const access
JSON_THROW(detail::out_of_range::create(402,
"array index '-' (" + std::to_string(ptr->m_value.array->size()) +
") is out of range"));
}
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
{
JSON_THROW(detail::parse_error::create(106, 0,
"array index '" + reference_token +
"' must not begin with '0'"));
}
// use unchecked array access
JSON_TRY
{
ptr = &ptr->operator[](
static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
}
break;
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
}
}
return *ptr;
}
/*!
@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.402 if the array index '-' is used
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
NLOHMANN_BASIC_JSON_TPL_DECLARATION
const NLOHMANN_BASIC_JSON_TPL& get_checked(const NLOHMANN_BASIC_JSON_TPL* ptr) const;
const BasicJsonType& get_checked(const BasicJsonType* ptr) const
{
using size_type = typename BasicJsonType::size_type;
for (const auto& reference_token : reference_tokens)
{
switch (ptr->m_type)
{
case detail::value_t::object:
{
// note: at performs range check
ptr = &ptr->at(reference_token);
break;
}
case detail::value_t::array:
{
if (JSON_UNLIKELY(reference_token == "-"))
{
// "-" always fails the range check
JSON_THROW(detail::out_of_range::create(402,
"array index '-' (" + std::to_string(ptr->m_value.array->size()) +
") is out of range"));
}
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
{
JSON_THROW(detail::parse_error::create(106, 0,
"array index '" + reference_token +
"' must not begin with '0'"));
}
// note: at performs range check
JSON_TRY
{
ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
}
break;
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
}
}
return *ptr;
}
/*!
@brief split the string input to reference tokens
...
...
@@ -9224,10 +9467,57 @@ class json_pointer
@note Empty objects or arrays are flattened to `null`.
*/
NLOHMANN_BASIC_JSON_TPL_DECLARATION
static void flatten(const std::string& reference_string,
const NLOHMANN_BASIC_JSON_TPL& value,
NLOHMANN_BASIC_JSON_TPL& result);
const BasicJsonType& value,
BasicJsonType& result)
{
switch (value.m_type)
{
case detail::value_t::array:
{
if (value.m_value.array->empty())
{
// flatten empty array as null
result[reference_string] = nullptr;
}
else
{
// iterate array and use index as reference string
for (std::size_t i = 0; i < value.m_value.array->size(); ++i)
{
flatten(reference_string + "/" + std::to_string(i),
value.m_value.array->operator[](i), result);
}
}
break;
}
case detail::value_t::object:
{
if (value.m_value.object->empty())
{
// flatten empty object as null
result[reference_string] = nullptr;
}
else
{
// iterate object and use keys as reference string
for (const auto& element : *value.m_value.object)
{
flatten(reference_string + "/" + escape(element.first), element.second, result);
}
}
break;
}
default:
{
// add primitive value with its reference string
result[reference_string] = value;
break;
}
}
}
/*!
@param[in] value flattened JSON
...
...
@@ -9239,19 +9529,108 @@ class json_pointer
@throw type_error.315 if object values are not primitive
@throw type_error.313 if value cannot be unflattened
*/
NLOHMANN_BASIC_JSON_TPL_DECLARATION
static NLOHMANN_BASIC_JSON_TPL
unflatten(const NLOHMANN_BASIC_JSON_TPL& value);
static BasicJsonType
unflatten(const BasicJsonType& value)
{
if (JSON_UNLIKELY(not value.is_object()))
{
JSON_THROW(detail::type_error::create(314, "only objects can be unflattened"));
}
BasicJsonType result;
// iterate the JSON object values
for (const auto& element : *value.m_value.object)
{
if (JSON_UNLIKELY(not element.second.is_primitive()))
{
JSON_THROW(detail::type_error::create(315, "values in object must be primitive"));
}
// assign value to reference pointed to by JSON pointer; Note that if
// the JSON pointer is "" (i.e., points to the whole value), function
// get_and_create returns a reference to result itself. An assignment
// will then create a primitive value.
json_pointer(element.first).get_and_create(result) = element.second;
}
return result;
}
friend bool operator==(json_pointer const& lhs,
json_pointer const& rhs) noexcept;
json_pointer const& rhs) noexcept
{
return (lhs.reference_tokens == rhs.reference_tokens);
}
friend bool operator!=(json_pointer const& lhs,
json_pointer const& rhs) noexcept;
json_pointer const& rhs) noexcept
{
return not (lhs == rhs);
}
/// the reference tokens
std::vector<std::string> reference_tokens;
};
}
// #include "adl_serializer.hpp"
#include <utility>
// #include "detail/conversions/from_json.hpp"
// #include "detail/conversions/to_json.hpp"
namespace nlohmann
{
template<typename, typename>
struct adl_serializer
{
/*!
@brief convert a JSON value to any value type
This function is usually called by the `get()` function of the
@ref basic_json class (either explicit or via conversion operators).
@param[in] j JSON value to read from
@param[in,out] val value to write to
*/
template<typename BasicJsonType, typename ValueType>
static void from_json(BasicJsonType&& j, ValueType& val) noexcept(
noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))
{
::nlohmann::from_json(std::forward<BasicJsonType>(j), val);
}
/*!
@brief convert any value type to a JSON value
This function is usually called by the constructors of the @ref basic_json
class.
@param[in,out] j JSON value to write to
@param[in] val value to read from
*/
template<typename BasicJsonType, typename ValueType>
static void to_json(BasicJsonType& j, ValueType&& val) noexcept(
noexcept(::nlohmann::to_json(j, std::forward<ValueType>(val))))
{
::nlohmann::to_json(j, std::forward<ValueType>(val));
}
};
}
/*!
@brief namespace for Niels Lohmann
@see https://github.com/nlohmann
@since version 1.0.0
*/
namespace nlohmann
{
/*!
@brief a class to store JSON values
...
...
@@ -9339,7 +9718,7 @@ class basic_json
{
private:
template<detail::value_t> friend struct detail::external_constructor;
friend ::nlohmann::json_pointer;
friend ::nlohmann::json_pointer
<basic_json>
;
friend ::nlohmann::detail::parser<basic_json>;
friend ::nlohmann::detail::serializer<basic_json>;
template<typename BasicJsonType>
...
...
@@ -9376,7 +9755,7 @@ class basic_json
public:
using value_t = detail::value_t;
/// @copydoc nlohmann::json_pointer
using json_pointer = ::nlohmann::json_pointer;
using json_pointer = ::nlohmann::json_pointer
<basic_json>
;
template<typename T, typename SFINAE>
using json_serializer = JSONSerializer<T, SFINAE>;
/// helper type for initializer lists of basic_json values
...
...
@@ -16642,400 +17021,6 @@ class basic_json
/// @}
};
//////////////////
// json_pointer //
//////////////////
NLOHMANN_BASIC_JSON_TPL_DECLARATION
NLOHMANN_BASIC_JSON_TPL&
json_pointer::get_and_create(NLOHMANN_BASIC_JSON_TPL& j) const
{
using size_type = typename NLOHMANN_BASIC_JSON_TPL::size_type;
auto result = &j;
// in case no reference tokens exist, return a reference to the JSON value
// j which will be overwritten by a primitive value
for (const auto& reference_token : reference_tokens)
{
switch (result->m_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
result = &result->operator[](reference_token);
break;
}
case detail::value_t::array:
{
// create an entry in the array
JSON_TRY
{
result = &result->operator[](static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
}
break;
}
/*
The following code is only reached if there exists a reference
token _and_ the current value is primitive. In this case, we have
an error situation, because primitive values may only occur as
single value; that is, with an empty list of reference tokens.
*/
default:
JSON_THROW(detail::type_error::create(313, "invalid value to unflatten"));
}
}
return *result;
}
NLOHMANN_BASIC_JSON_TPL_DECLARATION
NLOHMANN_BASIC_JSON_TPL&
json_pointer::get_unchecked(NLOHMANN_BASIC_JSON_TPL* ptr) const
{
using size_type = typename NLOHMANN_BASIC_JSON_TPL::size_type;
for (const auto& reference_token : reference_tokens)
{
// convert null values to arrays or objects before continuing
if (ptr->m_type == detail::value_t::null)
{
// check if reference token is a number
const bool nums =
std::all_of(reference_token.begin(), reference_token.end(),
[](const char x)
{
return (x >= '0' and x <= '9');
});
// change value to array for numbers or "-" or to object otherwise
*ptr = (nums or reference_token == "-")
? detail::value_t::array
: detail::value_t::object;
}
switch (ptr->m_type)
{
case detail::value_t::object:
{
// use unchecked object access
ptr = &ptr->operator[](reference_token);
break;
}
case detail::value_t::array:
{
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
{
JSON_THROW(detail::parse_error::create(106, 0,
"array index '" + reference_token +
"' must not begin with '0'"));
}
if (reference_token == "-")
{
// explicitly treat "-" as index beyond the end
ptr = &ptr->operator[](ptr->m_value.array->size());
}
else
{
// convert array index to number; unchecked access
JSON_TRY
{
ptr = &ptr->operator[](
static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
}
}
break;
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
}
}
return *ptr;
}
NLOHMANN_BASIC_JSON_TPL_DECLARATION
NLOHMANN_BASIC_JSON_TPL&
json_pointer::get_checked(NLOHMANN_BASIC_JSON_TPL* ptr) const
{
using size_type = typename NLOHMANN_BASIC_JSON_TPL::size_type;
for (const auto& reference_token : reference_tokens)
{
switch (ptr->m_type)
{
case detail::value_t::object:
{
// note: at performs range check
ptr = &ptr->at(reference_token);
break;
}
case detail::value_t::array:
{
if (JSON_UNLIKELY(reference_token == "-"))
{
// "-" always fails the range check
JSON_THROW(detail::out_of_range::create(402,
"array index '-' (" + std::to_string(ptr->m_value.array->size()) +
") is out of range"));
}
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
{
JSON_THROW(detail::parse_error::create(106, 0,
"array index '" + reference_token +
"' must not begin with '0'"));
}
// note: at performs range check
JSON_TRY
{
ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
}
break;
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
}
}
return *ptr;
}
NLOHMANN_BASIC_JSON_TPL_DECLARATION
const NLOHMANN_BASIC_JSON_TPL&
json_pointer::get_unchecked(const NLOHMANN_BASIC_JSON_TPL* ptr) const
{
using size_type = typename NLOHMANN_BASIC_JSON_TPL::size_type;
for (const auto& reference_token : reference_tokens)
{
switch (ptr->m_type)
{
case detail::value_t::object:
{
// use unchecked object access
ptr = &ptr->operator[](reference_token);
break;
}
case detail::value_t::array:
{
if (JSON_UNLIKELY(reference_token == "-"))
{
// "-" cannot be used for const access
JSON_THROW(detail::out_of_range::create(402,
"array index '-' (" + std::to_string(ptr->m_value.array->size()) +
") is out of range"));
}
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
{
JSON_THROW(detail::parse_error::create(106, 0,
"array index '" + reference_token +
"' must not begin with '0'"));
}
// use unchecked array access
JSON_TRY
{
ptr = &ptr->operator[](
static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
}
break;
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
}
}
return *ptr;
}
NLOHMANN_BASIC_JSON_TPL_DECLARATION
const NLOHMANN_BASIC_JSON_TPL&
json_pointer::get_checked(const NLOHMANN_BASIC_JSON_TPL* ptr) const
{
using size_type = typename NLOHMANN_BASIC_JSON_TPL::size_type;
for (const auto& reference_token : reference_tokens)
{
switch (ptr->m_type)
{
case detail::value_t::object:
{
// note: at performs range check
ptr = &ptr->at(reference_token);
break;
}
case detail::value_t::array:
{
if (JSON_UNLIKELY(reference_token == "-"))
{
// "-" always fails the range check
JSON_THROW(detail::out_of_range::create(402,
"array index '-' (" + std::to_string(ptr->m_value.array->size()) +
") is out of range"));
}
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
{
JSON_THROW(detail::parse_error::create(106, 0,
"array index '" + reference_token +
"' must not begin with '0'"));
}
// note: at performs range check
JSON_TRY
{
ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
}
break;
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
}
}
return *ptr;
}
NLOHMANN_BASIC_JSON_TPL_DECLARATION
void json_pointer::flatten(const std::string& reference_string,
const NLOHMANN_BASIC_JSON_TPL& value,
NLOHMANN_BASIC_JSON_TPL& result)
{
switch (value.m_type)
{
case detail::value_t::array:
{
if (value.m_value.array->empty())
{
// flatten empty array as null
result[reference_string] = nullptr;
}
else
{
// iterate array and use index as reference string
for (std::size_t i = 0; i < value.m_value.array->size(); ++i)
{
flatten(reference_string + "/" + std::to_string(i),
value.m_value.array->operator[](i), result);
}
}
break;
}
case detail::value_t::object:
{
if (value.m_value.object->empty())
{
// flatten empty object as null
result[reference_string] = nullptr;
}
else
{
// iterate object and use keys as reference string
for (const auto& element : *value.m_value.object)
{
flatten(reference_string + "/" + escape(element.first), element.second, result);
}
}
break;
}
default:
{
// add primitive value with its reference string
result[reference_string] = value;
break;
}
}
}
NLOHMANN_BASIC_JSON_TPL_DECLARATION
NLOHMANN_BASIC_JSON_TPL
json_pointer::unflatten(const NLOHMANN_BASIC_JSON_TPL& value)
{
if (JSON_UNLIKELY(not value.is_object()))
{
JSON_THROW(detail::type_error::create(314, "only objects can be unflattened"));
}
NLOHMANN_BASIC_JSON_TPL result;
// iterate the JSON object values
for (const auto& element : *value.m_value.object)
{
if (JSON_UNLIKELY(not element.second.is_primitive()))
{
JSON_THROW(detail::type_error::create(315, "values in object must be primitive"));
}
// assign value to reference pointed to by JSON pointer; Note that if
// the JSON pointer is "" (i.e., points to the whole value), function
// get_and_create returns a reference to result itself. An assignment
// will then create a primitive value.
json_pointer(element.first).get_and_create(result) = element.second;
}
return result;
}
inline bool operator==(json_pointer const& lhs, json_pointer const& rhs) noexcept
{
return (lhs.reference_tokens == rhs.reference_tokens);
}
inline bool operator!=(json_pointer const& lhs, json_pointer const& rhs) noexcept
{
return not (lhs == rhs);
}
} // namespace nlohmann
///////////////////////
...
...
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