2022-07-20 12:38:07 +02:00
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
2022-08-12 15:04:06 +02:00
// | | |__ | | | | | | version 3.11.2
2022-07-20 12:38:07 +02:00
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
2023-11-26 15:51:19 +01:00
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
2022-07-20 12:38:07 +02:00
// SPDX-License-Identifier: MIT
2018-01-28 15:36:56 +01:00
# pragma once
2019-01-13 15:31:22 +01:00
# include <algorithm> // all_of
2019-06-30 10:03:08 +02:00
# include <cctype> // isdigit
2022-04-12 14:18:16 +02:00
# include <cerrno> // errno, ERANGE
# include <cstdlib> // strtoull
2022-07-28 22:12:23 +02:00
# ifndef JSON_NO_IO
# include <iosfwd> // ostream
# endif // JSON_NO_IO
2020-06-20 15:36:28 +02:00
# include <limits> // max
2018-01-28 15:36:56 +01:00
# include <numeric> // accumulate
# include <string> // string
2019-03-15 14:55:13 +01:00
# include <utility> // move
2018-01-28 15:36:56 +01:00
# include <vector> // vector
2018-01-29 11:21:11 +01:00
# include <nlohmann/detail/exceptions.hpp>
2019-03-17 12:01:49 +01:00
# include <nlohmann/detail/macro_scope.hpp>
2022-04-12 14:18:16 +02:00
# include <nlohmann/detail/string_concat.hpp>
2021-01-16 15:33:05 +01:00
# include <nlohmann/detail/string_escape.hpp>
2018-01-29 11:21:11 +01:00
# include <nlohmann/detail/value_t.hpp>
2018-01-28 15:36:56 +01:00
2022-07-30 21:59:13 +02:00
NLOHMANN_JSON_NAMESPACE_BEGIN
2021-12-29 13:41:01 +01:00
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
/// @sa https://json.nlohmann.me/api/json_pointer/
2022-04-12 14:18:16 +02:00
template < typename RefStringType >
2018-01-28 15:36:56 +01:00
class json_pointer
{
// allow basic_json to access private members
NLOHMANN_BASIC_JSON_TPL_DECLARATION
friend class basic_json ;
2022-04-12 14:18:16 +02:00
template < typename >
friend class json_pointer ;
template < typename T >
struct string_t_helper
{
using type = T ;
} ;
NLOHMANN_BASIC_JSON_TPL_DECLARATION
struct string_t_helper < NLOHMANN_BASIC_JSON_TPL >
{
using type = StringType ;
} ;
2018-01-28 15:36:56 +01:00
public :
2022-04-12 14:18:16 +02:00
// for backwards compatibility accept BasicJsonType
using string_t = typename string_t_helper < RefStringType > : : type ;
2021-12-29 13:41:01 +01:00
/// @brief create JSON pointer
/// @sa https://json.nlohmann.me/api/json_pointer/json_pointer/
2022-04-12 14:18:16 +02:00
explicit json_pointer ( const string_t & s = " " )
2018-01-28 15:36:56 +01:00
: reference_tokens ( split ( s ) )
{ }
2021-12-29 13:41:01 +01:00
/// @brief return a string representation of the JSON pointer
/// @sa https://json.nlohmann.me/api/json_pointer/to_string/
2022-04-12 14:18:16 +02:00
string_t to_string ( ) const
2018-01-28 15:36:56 +01:00
{
return std : : accumulate ( reference_tokens . begin ( ) , reference_tokens . end ( ) ,
2022-04-12 14:18:16 +02:00
string_t { } ,
[ ] ( const string_t & a , const string_t & b )
2018-01-28 15:36:56 +01:00
{
2022-04-12 14:18:16 +02:00
return detail : : concat ( a , ' / ' , detail : : escape ( b ) ) ;
2018-01-28 15:36:56 +01:00
} ) ;
}
2021-12-29 13:41:01 +01:00
/// @brief return a string representation of the JSON pointer
/// @sa https://json.nlohmann.me/api/json_pointer/operator_string/
2022-07-28 22:12:23 +02:00
JSON_HEDLEY_DEPRECATED_FOR ( 3.11 .0 , to_string ( ) )
2022-04-12 14:18:16 +02:00
operator string_t ( ) const
2018-01-28 15:36:56 +01:00
{
return to_string ( ) ;
}
2022-07-28 22:12:23 +02:00
# ifndef JSON_NO_IO
/// @brief write string representation of the JSON pointer to stream
/// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/
friend std : : ostream & operator < < ( std : : ostream & o , const json_pointer & ptr )
{
o < < ptr . to_string ( ) ;
return o ;
}
# endif
2021-12-29 13:41:01 +01:00
/// @brief append another JSON pointer at the end of this JSON pointer
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
2019-01-31 20:15:36 +01:00
json_pointer & operator / = ( const json_pointer & ptr )
{
2019-03-17 22:25:18 +01:00
reference_tokens . insert ( reference_tokens . end ( ) ,
ptr . reference_tokens . begin ( ) ,
ptr . reference_tokens . end ( ) ) ;
2019-01-31 20:15:36 +01:00
return * this ;
}
2021-12-29 13:41:01 +01:00
/// @brief append an unescaped reference token at the end of this JSON pointer
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
2022-04-12 14:18:16 +02:00
json_pointer & operator / = ( string_t token )
2019-01-31 20:15:36 +01:00
{
push_back ( std : : move ( token ) ) ;
return * this ;
}
2021-12-29 13:41:01 +01:00
/// @brief append an array index at the end of this JSON pointer
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
2020-03-19 11:52:40 +01:00
json_pointer & operator / = ( std : : size_t array_idx )
2019-01-31 20:15:36 +01:00
{
2020-03-19 11:52:40 +01:00
return * this / = std : : to_string ( array_idx ) ;
2019-01-31 20:15:36 +01:00
}
2021-12-29 13:41:01 +01:00
/// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
2019-03-19 10:06:35 +01:00
friend json_pointer operator / ( const json_pointer & lhs ,
const json_pointer & rhs )
2019-01-31 20:15:36 +01:00
{
2019-03-19 10:06:35 +01:00
return json_pointer ( lhs ) / = rhs ;
2019-01-31 20:15:36 +01:00
}
2021-12-29 13:41:01 +01:00
/// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
2022-04-12 14:18:16 +02:00
friend json_pointer operator / ( const json_pointer & lhs , string_t token ) // NOLINT(performance-unnecessary-value-param)
2019-01-31 20:15:36 +01:00
{
2021-12-29 13:41:01 +01:00
return json_pointer ( lhs ) / = std : : move ( token ) ;
2019-01-31 20:15:36 +01:00
}
2021-12-29 13:41:01 +01:00
/// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
friend json_pointer operator / ( const json_pointer & lhs , std : : size_t array_idx )
2019-01-31 20:15:36 +01:00
{
2021-12-29 13:41:01 +01:00
return json_pointer ( lhs ) / = array_idx ;
2019-01-31 20:15:36 +01:00
}
2021-12-29 13:41:01 +01:00
/// @brief returns the parent of this JSON pointer
/// @sa https://json.nlohmann.me/api/json_pointer/parent_pointer/
2019-02-25 10:10:45 +01:00
json_pointer parent_pointer ( ) const
{
if ( empty ( ) )
{
return * this ;
}
json_pointer res = * this ;
res . pop_back ( ) ;
return res ;
}
2021-12-29 13:41:01 +01:00
/// @brief remove last reference token
/// @sa https://json.nlohmann.me/api/json_pointer/pop_back/
2019-03-19 15:40:23 +01:00
void pop_back ( )
2018-01-28 15:36:56 +01:00
{
2019-07-01 22:37:30 +02:00
if ( JSON_HEDLEY_UNLIKELY ( empty ( ) ) )
2018-01-28 15:36:56 +01:00
{
2022-04-12 14:18:16 +02:00
JSON_THROW ( detail : : out_of_range : : create ( 405 , " JSON pointer has no parent " , nullptr ) ) ;
2018-01-28 15:36:56 +01:00
}
reference_tokens . pop_back ( ) ;
2019-03-19 15:40:23 +01:00
}
2021-12-29 13:41:01 +01:00
/// @brief return last reference token
/// @sa https://json.nlohmann.me/api/json_pointer/back/
2022-04-12 14:18:16 +02:00
const string_t & back ( ) const
2019-03-19 15:40:23 +01:00
{
2019-07-01 22:37:30 +02:00
if ( JSON_HEDLEY_UNLIKELY ( empty ( ) ) )
2019-03-19 15:40:23 +01:00
{
2022-04-12 14:18:16 +02:00
JSON_THROW ( detail : : out_of_range : : create ( 405 , " JSON pointer has no parent " , nullptr ) ) ;
2019-03-19 15:40:23 +01:00
}
return reference_tokens . back ( ) ;
2018-01-28 15:36:56 +01:00
}
2021-12-29 13:41:01 +01:00
/// @brief append an unescaped token at the end of the reference pointer
/// @sa https://json.nlohmann.me/api/json_pointer/push_back/
2022-04-12 14:18:16 +02:00
void push_back ( const string_t & token )
2019-01-24 16:46:51 +01:00
{
2019-01-31 20:15:36 +01:00
reference_tokens . push_back ( token ) ;
2019-01-24 16:46:51 +01:00
}
2021-12-29 13:41:01 +01:00
/// @brief append an unescaped token at the end of the reference pointer
/// @sa https://json.nlohmann.me/api/json_pointer/push_back/
2022-04-12 14:18:16 +02:00
void push_back ( string_t & & token )
2019-01-24 16:46:51 +01:00
{
2019-01-31 20:15:36 +01:00
reference_tokens . push_back ( std : : move ( token ) ) ;
2019-01-24 16:46:51 +01:00
}
2021-12-29 13:41:01 +01:00
/// @brief return whether pointer points to the root document
/// @sa https://json.nlohmann.me/api/json_pointer/empty/
2019-02-25 09:57:12 +01:00
bool empty ( ) const noexcept
2018-01-28 15:36:56 +01:00
{
return reference_tokens . empty ( ) ;
}
2019-02-25 09:57:12 +01:00
private :
2019-03-17 22:25:18 +01:00
/*!
@ param [ in ] s reference token to be converted into an array index
@ return integer representation of @ a s
2020-06-20 15:25:54 +02:00
@ throw parse_error .106 if an array index begins with ' 0 '
@ throw parse_error .109 if an array index begins not with a digit
2019-03-17 22:25:18 +01:00
@ throw out_of_range .404 if string @ a s could not be converted to an integer
2020-06-20 15:36:28 +02:00
@ throw out_of_range .410 if an array index exceeds size_type
2019-03-17 22:25:18 +01:00
*/
2022-04-12 14:18:16 +02:00
template < typename BasicJsonType >
static typename BasicJsonType : : size_type array_index ( const string_t & s )
2019-03-17 22:25:18 +01:00
{
2020-06-20 15:36:28 +02:00
using size_type = typename BasicJsonType : : size_type ;
2020-03-17 13:49:28 +01:00
// error condition (cf. RFC 6901, Sect. 4)
2020-06-03 14:20:36 +02:00
if ( JSON_HEDLEY_UNLIKELY ( s . size ( ) > 1 & & s [ 0 ] = = ' 0 ' ) )
2020-03-17 13:49:28 +01:00
{
2022-04-12 14:18:16 +02:00
JSON_THROW ( detail : : parse_error : : create ( 106 , 0 , detail : : concat ( " array index ' " , s , " ' must not begin with '0' " ) , nullptr ) ) ;
2020-03-17 13:49:28 +01:00
}
2020-04-07 04:00:21 +02:00
// error condition (cf. RFC 6901, Sect. 4)
2020-06-03 14:20:36 +02:00
if ( JSON_HEDLEY_UNLIKELY ( s . size ( ) > 1 & & ! ( s [ 0 ] > = ' 1 ' & & s [ 0 ] < = ' 9 ' ) ) )
2020-03-17 13:49:28 +01:00
{
2022-04-12 14:18:16 +02:00
JSON_THROW ( detail : : parse_error : : create ( 109 , 0 , detail : : concat ( " array index ' " , s , " ' is not a number " ) , nullptr ) ) ;
2020-03-17 13:49:28 +01:00
}
2022-04-12 14:18:16 +02:00
const char * p = s . c_str ( ) ;
char * p_end = nullptr ;
errno = 0 ; // strtoull doesn't reset errno
2022-09-13 12:58:26 +02:00
const unsigned long long res = std : : strtoull ( p , & p_end , 10 ) ; // NOLINT(runtime/int)
2022-04-12 14:18:16 +02:00
if ( p = = p_end // invalid input or empty string
| | errno = = ERANGE // out of range
| | JSON_HEDLEY_UNLIKELY ( static_cast < std : : size_t > ( p_end - p ) ! = s . size ( ) ) ) // incomplete read
2020-03-23 10:24:47 +01:00
{
2022-04-12 14:18:16 +02:00
JSON_THROW ( detail : : out_of_range : : create ( 404 , detail : : concat ( " unresolved reference token ' " , s , " ' " ) , nullptr ) ) ;
2019-03-17 22:25:18 +01:00
}
2020-06-20 15:36:28 +02:00
// only triggered on special platforms (like 32bit), see also
// https://github.com/nlohmann/json/pull/2203
2021-03-24 07:15:18 +01:00
if ( res > = static_cast < unsigned long long > ( ( std : : numeric_limits < size_type > : : max ) ( ) ) ) // NOLINT(runtime/int)
2020-06-20 15:36:28 +02:00
{
2022-04-12 14:18:16 +02:00
JSON_THROW ( detail : : out_of_range : : create ( 410 , detail : : concat ( " array index " , s , " exceeds size_type " ) , nullptr ) ) ; // LCOV_EXCL_LINE
2020-06-20 15:36:28 +02:00
}
return static_cast < size_type > ( res ) ;
2019-03-17 22:25:18 +01:00
}
2020-08-12 13:30:06 +02:00
JSON_PRIVATE_UNLESS_TESTED :
2018-01-28 15:36:56 +01:00
json_pointer top ( ) const
{
2019-07-01 22:37:30 +02:00
if ( JSON_HEDLEY_UNLIKELY ( empty ( ) ) )
2018-01-28 15:36:56 +01:00
{
2022-04-12 14:18:16 +02:00
JSON_THROW ( detail : : out_of_range : : create ( 405 , " JSON pointer has no parent " , nullptr ) ) ;
2018-01-28 15:36:56 +01:00
}
json_pointer result = * this ;
result . reference_tokens = { reference_tokens [ 0 ] } ;
return result ;
}
2020-08-12 13:30:06 +02:00
private :
2018-01-28 15:36:56 +01:00
/*!
@ 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
*/
2022-04-12 14:18:16 +02:00
template < typename BasicJsonType >
2018-01-28 15:36:56 +01:00
BasicJsonType & get_and_create ( BasicJsonType & j ) const
{
2021-03-24 07:15:18 +01:00
auto * result = & j ;
2018-01-28 15:36:56 +01:00
// 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 )
{
2019-06-30 10:03:08 +02:00
switch ( result - > type ( ) )
2018-01-28 15:36:56 +01:00
{
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
2022-04-12 14:18:16 +02:00
result = & result - > operator [ ] ( array_index < BasicJsonType > ( reference_token ) ) ;
2018-01-28 15:36:56 +01:00
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 .
*/
2021-08-13 13:43:51 +02:00
case detail : : value_t : : string :
case detail : : value_t : : boolean :
case detail : : value_t : : number_integer :
case detail : : value_t : : number_unsigned :
case detail : : value_t : : number_float :
case detail : : value_t : : binary :
case detail : : value_t : : discarded :
2018-01-28 15:36:56 +01:00
default :
2022-04-12 14:18:16 +02:00
JSON_THROW ( detail : : type_error : : create ( 313 , " invalid value to unflatten " , & j ) ) ;
2018-01-28 15:36:56 +01:00
}
}
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
*/
2022-04-12 14:18:16 +02:00
template < typename BasicJsonType >
2018-01-28 15:36:56 +01:00
BasicJsonType & get_unchecked ( BasicJsonType * ptr ) const
{
for ( const auto & reference_token : reference_tokens )
{
// convert null values to arrays or objects before continuing
2019-06-30 10:03:08 +02:00
if ( ptr - > is_null ( ) )
2018-01-28 15:36:56 +01:00
{
// check if reference token is a number
const bool nums =
std : : all_of ( reference_token . begin ( ) , reference_token . end ( ) ,
2019-06-30 10:03:08 +02:00
[ ] ( const unsigned char x )
2018-01-28 15:36:56 +01:00
{
2019-06-30 10:03:08 +02:00
return std : : isdigit ( x ) ;
2018-01-28 15:36:56 +01:00
} ) ;
// change value to array for numbers or "-" or to object otherwise
2020-06-03 14:20:36 +02:00
* ptr = ( nums | | reference_token = = " - " )
2018-01-28 15:36:56 +01:00
? detail : : value_t : : array
: detail : : value_t : : object ;
}
2019-06-30 10:03:08 +02:00
switch ( ptr - > type ( ) )
2018-01-28 15:36:56 +01:00
{
case detail : : value_t : : object :
{
// use unchecked object access
ptr = & ptr - > operator [ ] ( reference_token ) ;
break ;
}
case detail : : value_t : : array :
{
if ( reference_token = = " - " )
{
// explicitly treat "-" as index beyond the end
2023-03-08 13:43:45 +01:00
ptr = & ptr - > operator [ ] ( ptr - > m_data . m_value . array - > size ( ) ) ;
2018-01-28 15:36:56 +01:00
}
else
{
// convert array index to number; unchecked access
2022-04-12 14:18:16 +02:00
ptr = & ptr - > operator [ ] ( array_index < BasicJsonType > ( reference_token ) ) ;
2018-01-28 15:36:56 +01:00
}
break ;
}
2021-08-13 13:43:51 +02:00
case detail : : value_t : : null :
case detail : : value_t : : string :
case detail : : value_t : : boolean :
case detail : : value_t : : number_integer :
case detail : : value_t : : number_unsigned :
case detail : : value_t : : number_float :
case detail : : value_t : : binary :
case detail : : value_t : : discarded :
2018-01-28 15:36:56 +01:00
default :
2022-04-12 14:18:16 +02:00
JSON_THROW ( detail : : out_of_range : : create ( 404 , detail : : concat ( " unresolved reference token ' " , reference_token , " ' " ) , ptr ) ) ;
2018-01-28 15:36:56 +01:00
}
}
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
*/
2022-04-12 14:18:16 +02:00
template < typename BasicJsonType >
2018-01-28 15:36:56 +01:00
BasicJsonType & get_checked ( BasicJsonType * ptr ) const
{
for ( const auto & reference_token : reference_tokens )
{
2019-06-30 10:03:08 +02:00
switch ( ptr - > type ( ) )
2018-01-28 15:36:56 +01:00
{
case detail : : value_t : : object :
{
// note: at performs range check
ptr = & ptr - > at ( reference_token ) ;
break ;
}
case detail : : value_t : : array :
{
2019-07-01 22:37:30 +02:00
if ( JSON_HEDLEY_UNLIKELY ( reference_token = = " - " ) )
2018-01-28 15:36:56 +01:00
{
// "-" always fails the range check
2022-04-12 14:18:16 +02:00
JSON_THROW ( detail : : out_of_range : : create ( 402 , detail : : concat (
2023-03-08 13:43:45 +01:00
" array index '-' ( " , std : : to_string ( ptr - > m_data . m_value . array - > size ( ) ) ,
2022-04-12 14:18:16 +02:00
" ) is out of range " ) , ptr ) ) ;
2018-01-28 15:36:56 +01:00
}
// note: at performs range check
2022-04-12 14:18:16 +02:00
ptr = & ptr - > at ( array_index < BasicJsonType > ( reference_token ) ) ;
2018-01-28 15:36:56 +01:00
break ;
}
2021-08-12 16:33:41 +02:00
case detail : : value_t : : null :
case detail : : value_t : : string :
case detail : : value_t : : boolean :
case detail : : value_t : : number_integer :
case detail : : value_t : : number_unsigned :
case detail : : value_t : : number_float :
case detail : : value_t : : binary :
case detail : : value_t : : discarded :
2018-01-28 15:36:56 +01:00
default :
2022-04-12 14:18:16 +02:00
JSON_THROW ( detail : : out_of_range : : create ( 404 , detail : : concat ( " unresolved reference token ' " , reference_token , " ' " ) , ptr ) ) ;
2018-01-28 15:36:56 +01:00
}
}
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
*/
2022-04-12 14:18:16 +02:00
template < typename BasicJsonType >
2018-01-28 15:36:56 +01:00
const BasicJsonType & get_unchecked ( const BasicJsonType * ptr ) const
{
for ( const auto & reference_token : reference_tokens )
{
2019-06-30 10:03:08 +02:00
switch ( ptr - > type ( ) )
2018-01-28 15:36:56 +01:00
{
case detail : : value_t : : object :
{
// use unchecked object access
ptr = & ptr - > operator [ ] ( reference_token ) ;
break ;
}
case detail : : value_t : : array :
{
2019-07-01 22:37:30 +02:00
if ( JSON_HEDLEY_UNLIKELY ( reference_token = = " - " ) )
2018-01-28 15:36:56 +01:00
{
// "-" cannot be used for const access
2023-03-08 13:43:45 +01:00
JSON_THROW ( detail : : out_of_range : : create ( 402 , detail : : concat ( " array index '-' ( " , std : : to_string ( ptr - > m_data . m_value . array - > size ( ) ) , " ) is out of range " ) , ptr ) ) ;
2018-01-28 15:36:56 +01:00
}
// use unchecked array access
2022-04-12 14:18:16 +02:00
ptr = & ptr - > operator [ ] ( array_index < BasicJsonType > ( reference_token ) ) ;
2018-01-28 15:36:56 +01:00
break ;
}
2021-08-13 13:43:51 +02:00
case detail : : value_t : : null :
case detail : : value_t : : string :
case detail : : value_t : : boolean :
case detail : : value_t : : number_integer :
case detail : : value_t : : number_unsigned :
case detail : : value_t : : number_float :
case detail : : value_t : : binary :
case detail : : value_t : : discarded :
2018-01-28 15:36:56 +01:00
default :
2022-04-12 14:18:16 +02:00
JSON_THROW ( detail : : out_of_range : : create ( 404 , detail : : concat ( " unresolved reference token ' " , reference_token , " ' " ) , ptr ) ) ;
2018-01-28 15:36:56 +01:00
}
}
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
*/
2022-04-12 14:18:16 +02:00
template < typename BasicJsonType >
2018-01-28 15:36:56 +01:00
const BasicJsonType & get_checked ( const BasicJsonType * ptr ) const
{
for ( const auto & reference_token : reference_tokens )
{
2019-06-30 10:03:08 +02:00
switch ( ptr - > type ( ) )
2018-01-28 15:36:56 +01:00
{
case detail : : value_t : : object :
{
// note: at performs range check
ptr = & ptr - > at ( reference_token ) ;
break ;
}
case detail : : value_t : : array :
{
2019-07-01 22:37:30 +02:00
if ( JSON_HEDLEY_UNLIKELY ( reference_token = = " - " ) )
2018-01-28 15:36:56 +01:00
{
// "-" always fails the range check
2022-04-12 14:18:16 +02:00
JSON_THROW ( detail : : out_of_range : : create ( 402 , detail : : concat (
2023-03-08 13:43:45 +01:00
" array index '-' ( " , std : : to_string ( ptr - > m_data . m_value . array - > size ( ) ) ,
2022-04-12 14:18:16 +02:00
" ) is out of range " ) , ptr ) ) ;
2018-01-28 15:36:56 +01:00
}
// note: at performs range check
2022-04-12 14:18:16 +02:00
ptr = & ptr - > at ( array_index < BasicJsonType > ( reference_token ) ) ;
2018-01-28 15:36:56 +01:00
break ;
}
2021-08-13 13:43:51 +02:00
case detail : : value_t : : null :
case detail : : value_t : : string :
case detail : : value_t : : boolean :
case detail : : value_t : : number_integer :
case detail : : value_t : : number_unsigned :
case detail : : value_t : : number_float :
case detail : : value_t : : binary :
case detail : : value_t : : discarded :
2018-01-28 15:36:56 +01:00
default :
2022-04-12 14:18:16 +02:00
JSON_THROW ( detail : : out_of_range : : create ( 404 , detail : : concat ( " unresolved reference token ' " , reference_token , " ' " ) , ptr ) ) ;
2018-01-28 15:36:56 +01:00
}
}
return * ptr ;
}
2019-06-30 10:03:08 +02:00
/*!
@ throw parse_error .106 if an array index begins with ' 0 '
@ throw parse_error .109 if an array index was not a number
*/
2022-04-12 14:18:16 +02:00
template < typename BasicJsonType >
2019-06-30 10:03:08 +02:00
bool contains ( const BasicJsonType * ptr ) const
{
for ( const auto & reference_token : reference_tokens )
{
switch ( ptr - > type ( ) )
{
case detail : : value_t : : object :
{
2020-06-03 14:20:36 +02:00
if ( ! ptr - > contains ( reference_token ) )
2019-06-30 10:03:08 +02:00
{
// we did not find the key in the object
return false ;
}
ptr = & ptr - > operator [ ] ( reference_token ) ;
break ;
}
case detail : : value_t : : array :
{
2019-07-14 21:19:55 +02:00
if ( JSON_HEDLEY_UNLIKELY ( reference_token = = " - " ) )
2019-06-30 10:03:08 +02:00
{
// "-" always fails the range check
return false ;
}
2020-06-03 14:20:36 +02:00
if ( JSON_HEDLEY_UNLIKELY ( reference_token . size ( ) = = 1 & & ! ( " 0 " < = reference_token & & reference_token < = " 9 " ) ) )
2020-04-13 13:41:13 +02:00
{
// invalid char
return false ;
}
if ( JSON_HEDLEY_UNLIKELY ( reference_token . size ( ) > 1 ) )
{
2020-06-03 14:20:36 +02:00
if ( JSON_HEDLEY_UNLIKELY ( ! ( ' 1 ' < = reference_token [ 0 ] & & reference_token [ 0 ] < = ' 9 ' ) ) )
2020-04-13 13:41:13 +02:00
{
// first char should be between '1' and '9'
return false ;
}
for ( std : : size_t i = 1 ; i < reference_token . size ( ) ; i + + )
{
2020-06-03 14:20:36 +02:00
if ( JSON_HEDLEY_UNLIKELY ( ! ( ' 0 ' < = reference_token [ i ] & & reference_token [ i ] < = ' 9 ' ) ) )
2020-04-13 13:41:13 +02:00
{
// other char should be between '0' and '9'
return false ;
}
}
}
2019-06-30 10:03:08 +02:00
2022-04-12 14:18:16 +02:00
const auto idx = array_index < BasicJsonType > ( reference_token ) ;
2020-03-23 10:24:47 +01:00
if ( idx > = ptr - > size ( ) )
2019-06-30 10:03:08 +02:00
{
2020-03-23 10:24:47 +01:00
// index out of range
return false ;
2019-06-30 10:03:08 +02:00
}
2020-03-23 10:24:47 +01:00
ptr = & ptr - > operator [ ] ( idx ) ;
2019-06-30 10:03:08 +02:00
break ;
}
2021-08-12 16:33:41 +02:00
case detail : : value_t : : null :
case detail : : value_t : : string :
case detail : : value_t : : boolean :
case detail : : value_t : : number_integer :
case detail : : value_t : : number_unsigned :
case detail : : value_t : : number_float :
case detail : : value_t : : binary :
case detail : : value_t : : discarded :
2019-06-30 10:03:08 +02:00
default :
{
// we do not expect primitive values if there is still a
// reference token to process
return false ;
}
}
}
// no reference token left means we found a primitive value
return true ;
}
2018-01-28 15:36:56 +01:00
/*!
@ 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 '
*/
2022-04-12 14:18:16 +02:00
static std : : vector < string_t > split ( const string_t & reference_string )
2018-01-28 15:36:56 +01:00
{
2022-04-12 14:18:16 +02:00
std : : vector < string_t > result ;
2018-01-28 15:36:56 +01:00
// special case: empty reference string -> no reference tokens
if ( reference_string . empty ( ) )
{
return result ;
}
// check if nonempty reference string begins with slash
2019-07-01 22:37:30 +02:00
if ( JSON_HEDLEY_UNLIKELY ( reference_string [ 0 ] ! = ' / ' ) )
2018-01-28 15:36:56 +01:00
{
2022-04-12 14:18:16 +02:00
JSON_THROW ( detail : : parse_error : : create ( 107 , 1 , detail : : concat ( " JSON pointer must be empty or begin with '/' - was: ' " , reference_string , " ' " ) , nullptr ) ) ;
2018-01-28 15:36:56 +01:00
}
// 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 ;
2022-04-12 14:18:16 +02:00
// we can stop if start == 0 (if slash == string_t::npos)
2018-01-28 15:36:56 +01:00
start ! = 0 ;
// set the beginning of the next reference token
2022-04-12 14:18:16 +02:00
// (will eventually be 0 if slash == string_t::npos)
start = ( slash = = string_t : : npos ) ? 0 : slash + 1 ,
2018-01-28 15:36:56 +01:00
// 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 ( ' ~ ' ) ;
2022-04-12 14:18:16 +02:00
pos ! = string_t : : npos ;
2018-01-28 15:36:56 +01:00
pos = reference_token . find_first_of ( ' ~ ' , pos + 1 ) )
{
2020-07-06 12:22:31 +02:00
JSON_ASSERT ( reference_token [ pos ] = = ' ~ ' ) ;
2018-01-28 15:36:56 +01:00
// ~ must be followed by 0 or 1
2020-06-03 14:20:36 +02:00
if ( JSON_HEDLEY_UNLIKELY ( pos = = reference_token . size ( ) - 1 | |
( reference_token [ pos + 1 ] ! = ' 0 ' & &
2019-07-01 22:37:30 +02:00
reference_token [ pos + 1 ] ! = ' 1 ' ) ) )
2018-01-28 15:36:56 +01:00
{
2022-04-12 14:18:16 +02:00
JSON_THROW ( detail : : parse_error : : create ( 108 , 0 , " escape character '~' must be followed with '0' or '1' " , nullptr ) ) ;
2018-01-28 15:36:56 +01:00
}
}
// finally, store the reference token
2021-01-16 15:33:05 +01:00
detail : : unescape ( reference_token ) ;
2018-01-28 15:36:56 +01:00
result . push_back ( reference_token ) ;
}
return result ;
}
2020-08-12 13:30:06 +02:00
private :
2018-01-28 15:36:56 +01:00
/*!
@ 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 ` .
*/
2022-04-12 14:18:16 +02:00
template < typename BasicJsonType >
static void flatten ( const string_t & reference_string ,
2018-01-28 15:36:56 +01:00
const BasicJsonType & value ,
BasicJsonType & result )
{
2019-06-30 10:03:08 +02:00
switch ( value . type ( ) )
2018-01-28 15:36:56 +01:00
{
case detail : : value_t : : array :
{
2023-03-08 13:43:45 +01:00
if ( value . m_data . m_value . array - > empty ( ) )
2018-01-28 15:36:56 +01:00
{
// flatten empty array as null
result [ reference_string ] = nullptr ;
}
else
{
// iterate array and use index as reference string
2023-03-08 13:43:45 +01:00
for ( std : : size_t i = 0 ; i < value . m_data . m_value . array - > size ( ) ; + + i )
2018-01-28 15:36:56 +01:00
{
2022-04-12 14:18:16 +02:00
flatten ( detail : : concat ( reference_string , ' / ' , std : : to_string ( i ) ) ,
2023-03-08 13:43:45 +01:00
value . m_data . m_value . array - > operator [ ] ( i ) , result ) ;
2018-01-28 15:36:56 +01:00
}
}
break ;
}
case detail : : value_t : : object :
{
2023-03-08 13:43:45 +01:00
if ( value . m_data . m_value . object - > empty ( ) )
2018-01-28 15:36:56 +01:00
{
// flatten empty object as null
result [ reference_string ] = nullptr ;
}
else
{
// iterate object and use keys as reference string
2023-03-08 13:43:45 +01:00
for ( const auto & element : * value . m_data . m_value . object )
2018-01-28 15:36:56 +01:00
{
2022-04-12 14:18:16 +02:00
flatten ( detail : : concat ( reference_string , ' / ' , detail : : escape ( element . first ) ) , element . second , result ) ;
2018-01-28 15:36:56 +01:00
}
}
break ;
}
2021-08-13 13:43:51 +02:00
case detail : : value_t : : null :
case detail : : value_t : : string :
case detail : : value_t : : boolean :
case detail : : value_t : : number_integer :
case detail : : value_t : : number_unsigned :
case detail : : value_t : : number_float :
case detail : : value_t : : binary :
case detail : : value_t : : discarded :
2018-01-28 15:36:56 +01:00
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
*/
2022-04-12 14:18:16 +02:00
template < typename BasicJsonType >
2018-01-28 15:36:56 +01:00
static BasicJsonType
unflatten ( const BasicJsonType & value )
{
2020-06-03 14:20:36 +02:00
if ( JSON_HEDLEY_UNLIKELY ( ! value . is_object ( ) ) )
2018-01-28 15:36:56 +01:00
{
2022-04-12 14:18:16 +02:00
JSON_THROW ( detail : : type_error : : create ( 314 , " only objects can be unflattened " , & value ) ) ;
2018-01-28 15:36:56 +01:00
}
BasicJsonType result ;
// iterate the JSON object values
2023-03-08 13:43:45 +01:00
for ( const auto & element : * value . m_data . m_value . object )
2018-01-28 15:36:56 +01:00
{
2020-06-03 14:20:36 +02:00
if ( JSON_HEDLEY_UNLIKELY ( ! element . second . is_primitive ( ) ) )
2018-01-28 15:36:56 +01:00
{
2022-04-12 14:18:16 +02:00
JSON_THROW ( detail : : type_error : : create ( 315 , " values in object must be primitive " , & element . second ) ) ;
2018-01-28 15:36:56 +01:00
}
// 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 ;
}
2022-04-12 14:18:16 +02:00
// can't use conversion operator because of ambiguity
json_pointer < string_t > convert ( ) const &
{
json_pointer < string_t > result ;
result . reference_tokens = reference_tokens ;
return result ;
}
json_pointer < string_t > convert ( ) & &
{
json_pointer < string_t > result ;
result . reference_tokens = std : : move ( reference_tokens ) ;
return result ;
}
2022-08-05 14:08:27 +02:00
public :
2022-08-07 14:35:40 +02:00
# if JSON_HAS_THREE_WAY_COMPARISON
2022-08-05 14:08:27 +02:00
/// @brief compares two JSON pointers for equality
/// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
template < typename RefStringTypeRhs >
bool operator = = ( const json_pointer < RefStringTypeRhs > & rhs ) const noexcept
{
return reference_tokens = = rhs . reference_tokens ;
}
2019-03-19 10:06:35 +01:00
2022-08-05 14:08:27 +02:00
/// @brief compares JSON pointer and string for equality
/// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
2022-08-07 22:26:59 +02:00
JSON_HEDLEY_DEPRECATED_FOR ( 3.11 .2 , operator = = ( json_pointer ) )
2022-08-05 14:08:27 +02:00
bool operator = = ( const string_t & rhs ) const
{
return * this = = json_pointer ( rhs ) ;
}
2022-08-07 14:35:40 +02:00
/// @brief 3-way compares two JSON pointers
template < typename RefStringTypeRhs >
std : : strong_ordering operator < = > ( const json_pointer < RefStringTypeRhs > & rhs ) const noexcept // *NOPAD*
{
return reference_tokens < = > rhs . reference_tokens ; // *NOPAD*
}
2022-08-05 14:08:27 +02:00
# else
/// @brief compares two JSON pointers for equality
/// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
2022-04-12 14:18:16 +02:00
template < typename RefStringTypeLhs , typename RefStringTypeRhs >
// NOLINTNEXTLINE(readability-redundant-declaration)
2022-08-05 14:08:27 +02:00
friend bool operator = = ( const json_pointer < RefStringTypeLhs > & lhs ,
const json_pointer < RefStringTypeRhs > & rhs ) noexcept ;
2018-01-28 15:36:56 +01:00
2022-08-05 14:08:27 +02:00
/// @brief compares JSON pointer and string for equality
/// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
template < typename RefStringTypeLhs , typename StringType >
// NOLINTNEXTLINE(readability-redundant-declaration)
friend bool operator = = ( const json_pointer < RefStringTypeLhs > & lhs ,
const StringType & rhs ) ;
2019-03-19 10:06:35 +01:00
2022-08-05 14:08:27 +02:00
/// @brief compares string and JSON pointer for equality
/// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
template < typename RefStringTypeRhs , typename StringType >
// NOLINTNEXTLINE(readability-redundant-declaration)
friend bool operator = = ( const StringType & lhs ,
const json_pointer < RefStringTypeRhs > & rhs ) ;
2019-03-19 10:06:35 +01:00
2022-08-05 14:08:27 +02:00
/// @brief compares two JSON pointers for inequality
/// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
2022-04-12 14:18:16 +02:00
template < typename RefStringTypeLhs , typename RefStringTypeRhs >
// NOLINTNEXTLINE(readability-redundant-declaration)
2022-08-05 14:08:27 +02:00
friend bool operator ! = ( const json_pointer < RefStringTypeLhs > & lhs ,
const json_pointer < RefStringTypeRhs > & rhs ) noexcept ;
2018-01-28 15:36:56 +01:00
2022-08-05 14:08:27 +02:00
/// @brief compares JSON pointer and string for inequality
/// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
template < typename RefStringTypeLhs , typename StringType >
// NOLINTNEXTLINE(readability-redundant-declaration)
friend bool operator ! = ( const json_pointer < RefStringTypeLhs > & lhs ,
const StringType & rhs ) ;
/// @brief compares string and JSON pointer for inequality
/// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
template < typename RefStringTypeRhs , typename StringType >
// NOLINTNEXTLINE(readability-redundant-declaration)
friend bool operator ! = ( const StringType & lhs ,
const json_pointer < RefStringTypeRhs > & rhs ) ;
2022-08-07 14:35:40 +02:00
/// @brief compares two JSON pointer for less-than
template < typename RefStringTypeLhs , typename RefStringTypeRhs >
// NOLINTNEXTLINE(readability-redundant-declaration)
friend bool operator < ( const json_pointer < RefStringTypeLhs > & lhs ,
const json_pointer < RefStringTypeRhs > & rhs ) noexcept ;
2022-08-05 14:08:27 +02:00
# endif
private :
2018-01-28 15:36:56 +01:00
/// the reference tokens
2022-04-12 14:18:16 +02:00
std : : vector < string_t > reference_tokens ;
2018-01-28 15:36:56 +01:00
} ;
2022-04-12 14:18:16 +02:00
2022-08-07 14:35:40 +02:00
# if !JSON_HAS_THREE_WAY_COMPARISON
2022-04-12 14:18:16 +02:00
// functions cannot be defined inside class due to ODR violations
template < typename RefStringTypeLhs , typename RefStringTypeRhs >
2022-08-05 14:08:27 +02:00
inline bool operator = = ( const json_pointer < RefStringTypeLhs > & lhs ,
const json_pointer < RefStringTypeRhs > & rhs ) noexcept
2022-04-12 14:18:16 +02:00
{
return lhs . reference_tokens = = rhs . reference_tokens ;
}
2022-08-05 14:08:27 +02:00
template < typename RefStringTypeLhs ,
typename StringType = typename json_pointer < RefStringTypeLhs > : : string_t >
2022-08-07 22:26:59 +02:00
JSON_HEDLEY_DEPRECATED_FOR ( 3.11 .2 , operator = = ( json_pointer , json_pointer ) )
2022-08-05 14:08:27 +02:00
inline bool operator = = ( const json_pointer < RefStringTypeLhs > & lhs ,
const StringType & rhs )
{
return lhs = = json_pointer < RefStringTypeLhs > ( rhs ) ;
}
template < typename RefStringTypeRhs ,
typename StringType = typename json_pointer < RefStringTypeRhs > : : string_t >
2022-08-07 22:26:59 +02:00
JSON_HEDLEY_DEPRECATED_FOR ( 3.11 .2 , operator = = ( json_pointer , json_pointer ) )
2022-08-05 14:08:27 +02:00
inline bool operator = = ( const StringType & lhs ,
const json_pointer < RefStringTypeRhs > & rhs )
{
return json_pointer < RefStringTypeRhs > ( lhs ) = = rhs ;
}
2022-04-12 14:18:16 +02:00
template < typename RefStringTypeLhs , typename RefStringTypeRhs >
2022-08-05 14:08:27 +02:00
inline bool operator ! = ( const json_pointer < RefStringTypeLhs > & lhs ,
const json_pointer < RefStringTypeRhs > & rhs ) noexcept
2022-04-12 14:18:16 +02:00
{
return ! ( lhs = = rhs ) ;
}
2022-07-30 21:59:13 +02:00
2022-08-05 14:08:27 +02:00
template < typename RefStringTypeLhs ,
typename StringType = typename json_pointer < RefStringTypeLhs > : : string_t >
2022-08-07 22:26:59 +02:00
JSON_HEDLEY_DEPRECATED_FOR ( 3.11 .2 , operator ! = ( json_pointer , json_pointer ) )
2022-08-05 14:08:27 +02:00
inline bool operator ! = ( const json_pointer < RefStringTypeLhs > & lhs ,
const StringType & rhs )
{
return ! ( lhs = = rhs ) ;
}
template < typename RefStringTypeRhs ,
typename StringType = typename json_pointer < RefStringTypeRhs > : : string_t >
2022-08-07 22:26:59 +02:00
JSON_HEDLEY_DEPRECATED_FOR ( 3.11 .2 , operator ! = ( json_pointer , json_pointer ) )
2022-08-05 14:08:27 +02:00
inline bool operator ! = ( const StringType & lhs ,
const json_pointer < RefStringTypeRhs > & rhs )
{
return ! ( lhs = = rhs ) ;
}
2022-08-07 14:35:40 +02:00
template < typename RefStringTypeLhs , typename RefStringTypeRhs >
inline bool operator < ( const json_pointer < RefStringTypeLhs > & lhs ,
const json_pointer < RefStringTypeRhs > & rhs ) noexcept
{
return lhs . reference_tokens < rhs . reference_tokens ;
}
2022-08-05 14:08:27 +02:00
# endif
2022-07-30 21:59:13 +02:00
NLOHMANN_JSON_NAMESPACE_END