2022-07-20 13:38:07 +03:00
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.10.5
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
2016-08-04 22:55:47 +03:00
2019-01-13 19:41:21 +03:00
# include "doctest_compatibility.h"
2016-08-04 22:55:47 +03:00
2020-08-12 14:30:06 +03:00
# define JSON_TESTS_PRIVATE
2018-01-29 13:21:11 +03:00
# include <nlohmann/json.hpp>
2016-08-04 22:55:47 +03:00
using nlohmann : : json ;
2022-07-31 18:38:52 +03:00
using namespace nlohmann : : literals ; // NOLINT(google-build-using-namespace)
2016-08-04 22:55:47 +03:00
2022-07-28 23:12:23 +03:00
# include <sstream>
2016-08-04 22:55:47 +03:00
TEST_CASE ( " JSON pointers " )
{
SECTION ( " errors " )
{
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( json : : json_pointer ( " foo " ) ,
" [json.exception.parse_error.107] parse error at byte 1: JSON pointer must be empty or begin with '/' - was: 'foo' " , json : : parse_error & ) ;
2016-08-04 22:55:47 +03:00
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( json : : json_pointer ( " /~~ " ) ,
" [json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1' " , json : : parse_error & ) ;
2016-08-04 22:55:47 +03:00
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( json : : json_pointer ( " /~ " ) ,
" [json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1' " , json : : parse_error & ) ;
2016-08-04 22:55:47 +03:00
json : : json_pointer p ;
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( p . top ( ) ,
" [json.exception.out_of_range.405] JSON pointer has no parent " , json : : out_of_range & ) ;
CHECK_THROWS_WITH_AS ( p . pop_back ( ) ,
" [json.exception.out_of_range.405] JSON pointer has no parent " , json : : out_of_range & ) ;
2017-12-29 18:53:04 +03:00
SECTION ( " array index error " )
{
json v = { 1 , 2 , 3 , 4 } ;
json : : json_pointer ptr ( " /10e " ) ;
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( v [ ptr ] ,
" [json.exception.out_of_range.404] unresolved reference token '10e' " , json : : out_of_range & ) ;
2017-12-29 18:53:04 +03:00
}
2016-08-04 22:55:47 +03:00
}
SECTION ( " examples from RFC 6901 " )
{
SECTION ( " nonconst access " )
{
json j = R " (
{
" foo " : [ " bar " , " baz " ] ,
" " : 0 ,
" a/b " : 1 ,
" c%d " : 2 ,
" e^f " : 3 ,
" g|h " : 4 ,
" i \\ j " : 5 ,
" k \" l " : 6 ,
" " : 7 ,
" m~n " : 8
}
) " _json;
// the whole document
CHECK ( j [ json : : json_pointer ( ) ] = = j ) ;
CHECK ( j [ json : : json_pointer ( " " ) ] = = j ) ;
2019-06-30 11:03:08 +03:00
CHECK ( j . contains ( json : : json_pointer ( ) ) ) ;
CHECK ( j . contains ( json : : json_pointer ( " " ) ) ) ;
2016-08-04 22:55:47 +03:00
// array access
CHECK ( j [ json : : json_pointer ( " /foo " ) ] = = j [ " foo " ] ) ;
2019-06-30 11:03:08 +03:00
CHECK ( j . contains ( json : : json_pointer ( " /foo " ) ) ) ;
2016-08-04 22:55:47 +03:00
CHECK ( j [ json : : json_pointer ( " /foo/0 " ) ] = = j [ " foo " ] [ 0 ] ) ;
CHECK ( j [ json : : json_pointer ( " /foo/1 " ) ] = = j [ " foo " ] [ 1 ] ) ;
CHECK ( j [ " /foo/1 " _json_pointer ] = = j [ " foo " ] [ 1 ] ) ;
2019-06-30 11:03:08 +03:00
CHECK ( j . contains ( json : : json_pointer ( " /foo/0 " ) ) ) ;
CHECK ( j . contains ( json : : json_pointer ( " /foo/1 " ) ) ) ;
2020-06-03 22:22:07 +03:00
CHECK ( ! j . contains ( json : : json_pointer ( " /foo/3 " ) ) ) ;
CHECK ( ! j . contains ( json : : json_pointer ( " /foo/+ " ) ) ) ;
CHECK ( ! j . contains ( json : : json_pointer ( " /foo/1+2 " ) ) ) ;
CHECK ( ! j . contains ( json : : json_pointer ( " /foo/- " ) ) ) ;
2016-08-04 22:55:47 +03:00
// checked array access
CHECK ( j . at ( json : : json_pointer ( " /foo/0 " ) ) = = j [ " foo " ] [ 0 ] ) ;
CHECK ( j . at ( json : : json_pointer ( " /foo/1 " ) ) = = j [ " foo " ] [ 1 ] ) ;
// empty string access
CHECK ( j [ json : : json_pointer ( " / " ) ] = = j [ " " ] ) ;
2019-06-30 11:03:08 +03:00
CHECK ( j . contains ( json : : json_pointer ( " " ) ) ) ;
CHECK ( j . contains ( json : : json_pointer ( " / " ) ) ) ;
2016-08-04 22:55:47 +03:00
// other cases
CHECK ( j [ json : : json_pointer ( " / " ) ] = = j [ " " ] ) ;
CHECK ( j [ json : : json_pointer ( " /c%d " ) ] = = j [ " c%d " ] ) ;
CHECK ( j [ json : : json_pointer ( " /e^f " ) ] = = j [ " e^f " ] ) ;
CHECK ( j [ json : : json_pointer ( " /g|h " ) ] = = j [ " g|h " ] ) ;
CHECK ( j [ json : : json_pointer ( " /i \\ j " ) ] = = j [ " i \\ j " ] ) ;
CHECK ( j [ json : : json_pointer ( " /k \" l " ) ] = = j [ " k \" l " ] ) ;
2019-06-30 11:03:08 +03:00
// contains
CHECK ( j . contains ( json : : json_pointer ( " / " ) ) ) ;
CHECK ( j . contains ( json : : json_pointer ( " /c%d " ) ) ) ;
CHECK ( j . contains ( json : : json_pointer ( " /e^f " ) ) ) ;
CHECK ( j . contains ( json : : json_pointer ( " /g|h " ) ) ) ;
CHECK ( j . contains ( json : : json_pointer ( " /i \\ j " ) ) ) ;
CHECK ( j . contains ( json : : json_pointer ( " /k \" l " ) ) ) ;
2016-08-04 22:55:47 +03:00
// checked access
CHECK ( j . at ( json : : json_pointer ( " / " ) ) = = j [ " " ] ) ;
CHECK ( j . at ( json : : json_pointer ( " /c%d " ) ) = = j [ " c%d " ] ) ;
CHECK ( j . at ( json : : json_pointer ( " /e^f " ) ) = = j [ " e^f " ] ) ;
CHECK ( j . at ( json : : json_pointer ( " /g|h " ) ) = = j [ " g|h " ] ) ;
CHECK ( j . at ( json : : json_pointer ( " /i \\ j " ) ) = = j [ " i \\ j " ] ) ;
CHECK ( j . at ( json : : json_pointer ( " /k \" l " ) ) = = j [ " k \" l " ] ) ;
// escaped access
CHECK ( j [ json : : json_pointer ( " /a~1b " ) ] = = j [ " a/b " ] ) ;
CHECK ( j [ json : : json_pointer ( " /m~0n " ) ] = = j [ " m~n " ] ) ;
2019-06-30 11:03:08 +03:00
CHECK ( j . contains ( json : : json_pointer ( " /a~1b " ) ) ) ;
CHECK ( j . contains ( json : : json_pointer ( " /m~0n " ) ) ) ;
2016-08-04 22:55:47 +03:00
// unescaped access
2016-10-08 15:27:28 +03:00
// access to nonexisting values yield object creation
2020-06-03 22:22:07 +03:00
CHECK ( ! j . contains ( json : : json_pointer ( " /a/b " ) ) ) ;
2016-10-08 15:27:28 +03:00
CHECK_NOTHROW ( j [ json : : json_pointer ( " /a/b " ) ] = 42 ) ;
2019-06-30 11:03:08 +03:00
CHECK ( j . contains ( json : : json_pointer ( " /a/b " ) ) ) ;
2016-10-08 15:27:28 +03:00
CHECK ( j [ " a " ] [ " b " ] = = json ( 42 ) ) ;
2019-06-30 11:03:08 +03:00
2020-06-03 22:22:07 +03:00
CHECK ( ! j . contains ( json : : json_pointer ( " /a/c/1 " ) ) ) ;
2016-10-08 15:27:28 +03:00
CHECK_NOTHROW ( j [ json : : json_pointer ( " /a/c/1 " ) ] = 42 ) ;
CHECK ( j [ " a " ] [ " c " ] = = json ( { nullptr , 42 } ) ) ;
2019-06-30 11:03:08 +03:00
CHECK ( j . contains ( json : : json_pointer ( " /a/c/1 " ) ) ) ;
2020-06-03 22:22:07 +03:00
CHECK ( ! j . contains ( json : : json_pointer ( " /a/d/- " ) ) ) ;
2016-10-08 15:27:28 +03:00
CHECK_NOTHROW ( j [ json : : json_pointer ( " /a/d/- " ) ] = 42 ) ;
2020-06-03 22:22:07 +03:00
CHECK ( ! j . contains ( json : : json_pointer ( " /a/d/- " ) ) ) ;
2016-10-08 15:27:28 +03:00
CHECK ( j [ " a " ] [ " d " ] = = json : : array ( { 42 } ) ) ;
2016-08-04 22:55:47 +03:00
// "/a/b" works for JSON {"a": {"b": 42}}
CHECK ( json ( { { " a " , { { " b " , 42 } } } } ) [ json : : json_pointer ( " /a/b " ) ] = = json ( 42 ) ) ;
// unresolved access
json j_primitive = 1 ;
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( j_primitive [ " /foo " _json_pointer ] ,
" [json.exception.out_of_range.404] unresolved reference token 'foo' " , json : : out_of_range & ) ;
CHECK_THROWS_WITH_AS ( j_primitive . at ( " /foo " _json_pointer ) ,
" [json.exception.out_of_range.404] unresolved reference token 'foo' " , json : : out_of_range & ) ;
2020-06-03 22:22:07 +03:00
CHECK ( ! j_primitive . contains ( json : : json_pointer ( " /foo " ) ) ) ;
2016-08-04 22:55:47 +03:00
}
SECTION ( " const access " )
{
const json j = R " (
{
" foo " : [ " bar " , " baz " ] ,
" " : 0 ,
" a/b " : 1 ,
" c%d " : 2 ,
" e^f " : 3 ,
" g|h " : 4 ,
" i \\ j " : 5 ,
" k \" l " : 6 ,
" " : 7 ,
" m~n " : 8
}
) " _json;
// the whole document
CHECK ( j [ json : : json_pointer ( ) ] = = j ) ;
CHECK ( j [ json : : json_pointer ( " " ) ] = = j ) ;
// array access
CHECK ( j [ json : : json_pointer ( " /foo " ) ] = = j [ " foo " ] ) ;
CHECK ( j [ json : : json_pointer ( " /foo/0 " ) ] = = j [ " foo " ] [ 0 ] ) ;
CHECK ( j [ json : : json_pointer ( " /foo/1 " ) ] = = j [ " foo " ] [ 1 ] ) ;
CHECK ( j [ " /foo/1 " _json_pointer ] = = j [ " foo " ] [ 1 ] ) ;
// checked array access
CHECK ( j . at ( json : : json_pointer ( " /foo/0 " ) ) = = j [ " foo " ] [ 0 ] ) ;
CHECK ( j . at ( json : : json_pointer ( " /foo/1 " ) ) = = j [ " foo " ] [ 1 ] ) ;
// empty string access
CHECK ( j [ json : : json_pointer ( " / " ) ] = = j [ " " ] ) ;
// other cases
CHECK ( j [ json : : json_pointer ( " / " ) ] = = j [ " " ] ) ;
CHECK ( j [ json : : json_pointer ( " /c%d " ) ] = = j [ " c%d " ] ) ;
CHECK ( j [ json : : json_pointer ( " /e^f " ) ] = = j [ " e^f " ] ) ;
CHECK ( j [ json : : json_pointer ( " /g|h " ) ] = = j [ " g|h " ] ) ;
CHECK ( j [ json : : json_pointer ( " /i \\ j " ) ] = = j [ " i \\ j " ] ) ;
CHECK ( j [ json : : json_pointer ( " /k \" l " ) ] = = j [ " k \" l " ] ) ;
// checked access
CHECK ( j . at ( json : : json_pointer ( " / " ) ) = = j [ " " ] ) ;
CHECK ( j . at ( json : : json_pointer ( " /c%d " ) ) = = j [ " c%d " ] ) ;
CHECK ( j . at ( json : : json_pointer ( " /e^f " ) ) = = j [ " e^f " ] ) ;
CHECK ( j . at ( json : : json_pointer ( " /g|h " ) ) = = j [ " g|h " ] ) ;
CHECK ( j . at ( json : : json_pointer ( " /i \\ j " ) ) = = j [ " i \\ j " ] ) ;
CHECK ( j . at ( json : : json_pointer ( " /k \" l " ) ) = = j [ " k \" l " ] ) ;
// escaped access
CHECK ( j [ json : : json_pointer ( " /a~1b " ) ] = = j [ " a/b " ] ) ;
CHECK ( j [ json : : json_pointer ( " /m~0n " ) ] = = j [ " m~n " ] ) ;
// unescaped access
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( j . at ( json : : json_pointer ( " /a/b " ) ) ,
" [json.exception.out_of_range.403] key 'a' not found " , json : : out_of_range & ) ;
2016-08-04 22:55:47 +03:00
// unresolved access
const json j_primitive = 1 ;
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( j_primitive [ " /foo " _json_pointer ] ,
" [json.exception.out_of_range.404] unresolved reference token 'foo' " , json : : out_of_range & ) ;
CHECK_THROWS_WITH_AS ( j_primitive . at ( " /foo " _json_pointer ) ,
" [json.exception.out_of_range.404] unresolved reference token 'foo' " , json : : out_of_range & ) ;
2016-08-04 22:55:47 +03:00
}
SECTION ( " user-defined string literal " )
{
json j = R " (
{
" foo " : [ " bar " , " baz " ] ,
" " : 0 ,
" a/b " : 1 ,
" c%d " : 2 ,
" e^f " : 3 ,
" g|h " : 4 ,
" i \\ j " : 5 ,
" k \" l " : 6 ,
" " : 7 ,
" m~n " : 8
}
) " _json;
// the whole document
CHECK ( j [ " " _json_pointer ] = = j ) ;
2019-06-30 11:03:08 +03:00
CHECK ( j . contains ( " " _json_pointer ) ) ;
2016-08-04 22:55:47 +03:00
// array access
CHECK ( j [ " /foo " _json_pointer ] = = j [ " foo " ] ) ;
CHECK ( j [ " /foo/0 " _json_pointer ] = = j [ " foo " ] [ 0 ] ) ;
CHECK ( j [ " /foo/1 " _json_pointer ] = = j [ " foo " ] [ 1 ] ) ;
2019-06-30 11:03:08 +03:00
CHECK ( j . contains ( " /foo " _json_pointer ) ) ;
CHECK ( j . contains ( " /foo/0 " _json_pointer ) ) ;
CHECK ( j . contains ( " /foo/1 " _json_pointer ) ) ;
2020-06-03 22:22:07 +03:00
CHECK ( ! j . contains ( " /foo/- " _json_pointer ) ) ;
2016-08-04 22:55:47 +03:00
}
}
SECTION ( " array access " )
{
SECTION ( " nonconst access " )
{
json j = { 1 , 2 , 3 } ;
const json j_const = j ;
// check reading access
CHECK ( j [ " /0 " _json_pointer ] = = j [ 0 ] ) ;
CHECK ( j [ " /1 " _json_pointer ] = = j [ 1 ] ) ;
CHECK ( j [ " /2 " _json_pointer ] = = j [ 2 ] ) ;
// assign to existing index
j [ " /1 " _json_pointer ] = 13 ;
CHECK ( j [ 1 ] = = json ( 13 ) ) ;
// assign to nonexisting index
j [ " /3 " _json_pointer ] = 33 ;
CHECK ( j [ 3 ] = = json ( 33 ) ) ;
// assign to nonexisting index (with gap)
j [ " /5 " _json_pointer ] = 55 ;
CHECK ( j = = json ( { 1 , 13 , 3 , 33 , nullptr , 55 } ) ) ;
// error with leading 0
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( j [ " /01 " _json_pointer ] ,
" [json.exception.parse_error.106] parse error: array index '01' must not begin with '0' " , json : : parse_error & ) ;
CHECK_THROWS_WITH_AS ( j_const [ " /01 " _json_pointer ] ,
" [json.exception.parse_error.106] parse error: array index '01' must not begin with '0' " , json : : parse_error & ) ;
CHECK_THROWS_WITH_AS ( j . at ( " /01 " _json_pointer ) ,
" [json.exception.parse_error.106] parse error: array index '01' must not begin with '0' " , json : : parse_error & ) ;
CHECK_THROWS_WITH_AS ( j_const . at ( " /01 " _json_pointer ) ,
" [json.exception.parse_error.106] parse error: array index '01' must not begin with '0' " , json : : parse_error & ) ;
2020-04-13 14:41:13 +03:00
2020-06-03 22:22:07 +03:00
CHECK ( ! j . contains ( " /01 " _json_pointer ) ) ;
CHECK ( ! j . contains ( " /01 " _json_pointer ) ) ;
CHECK ( ! j_const . contains ( " /01 " _json_pointer ) ) ;
CHECK ( ! j_const . contains ( " /01 " _json_pointer ) ) ;
2016-08-04 22:55:47 +03:00
// error with incorrect numbers
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( j [ " /one " _json_pointer ] = 1 ,
" [json.exception.parse_error.109] parse error: array index 'one' is not a number " , json : : parse_error & ) ;
CHECK_THROWS_WITH_AS ( j_const [ " /one " _json_pointer ] = = 1 ,
" [json.exception.parse_error.109] parse error: array index 'one' is not a number " , json : : parse_error & ) ;
CHECK_THROWS_WITH_AS ( j . at ( " /one " _json_pointer ) = 1 ,
" [json.exception.parse_error.109] parse error: array index 'one' is not a number " , json : : parse_error & ) ;
CHECK_THROWS_WITH_AS ( j_const . at ( " /one " _json_pointer ) = = 1 ,
" [json.exception.parse_error.109] parse error: array index 'one' is not a number " , json : : parse_error & ) ;
CHECK_THROWS_WITH_AS ( j [ " /+1 " _json_pointer ] = 1 ,
" [json.exception.parse_error.109] parse error: array index '+1' is not a number " , json : : parse_error & ) ;
CHECK_THROWS_WITH_AS ( j_const [ " /+1 " _json_pointer ] = = 1 ,
" [json.exception.parse_error.109] parse error: array index '+1' is not a number " , json : : parse_error & ) ;
CHECK_THROWS_WITH_AS ( j [ " /1+1 " _json_pointer ] = 1 ,
" [json.exception.out_of_range.404] unresolved reference token '1+1' " , json : : out_of_range & ) ;
CHECK_THROWS_WITH_AS ( j_const [ " /1+1 " _json_pointer ] = = 1 ,
" [json.exception.out_of_range.404] unresolved reference token '1+1' " , json : : out_of_range & ) ;
2020-03-25 10:56:45 +03:00
2020-06-20 16:36:28 +03:00
{
auto too_large_index = std : : to_string ( ( std : : numeric_limits < unsigned long long > : : max ) ( ) ) + " 1 " ;
json : : json_pointer jp ( std : : string ( " / " ) + too_large_index ) ;
std : : string throw_msg = std : : string ( " [json.exception.out_of_range.404] unresolved reference token ' " ) + too_large_index + " ' " ;
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( j [ jp ] = 1 , throw_msg . c_str ( ) , json : : out_of_range & ) ;
CHECK_THROWS_WITH_AS ( j_const [ jp ] = = 1 , throw_msg . c_str ( ) , json : : out_of_range & ) ;
2020-06-20 16:36:28 +03:00
}
2021-08-17 15:43:43 +03:00
// on some machines, the check below is not constant
DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
DOCTEST_MSVC_SUPPRESS_WARNING ( 4127 )
2020-06-20 16:36:28 +03:00
if ( sizeof ( typename json : : size_type ) < sizeof ( unsigned long long ) )
{
auto size_type_max_uul = static_cast < unsigned long long > ( ( std : : numeric_limits < json : : size_type > : : max ) ( ) ) ;
auto too_large_index = std : : to_string ( size_type_max_uul ) ;
json : : json_pointer jp ( std : : string ( " / " ) + too_large_index ) ;
std : : string throw_msg = std : : string ( " [json.exception.out_of_range.410] array index " ) + too_large_index + " exceeds size_type " ;
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( j [ jp ] = 1 , throw_msg . c_str ( ) , json : : out_of_range & ) ;
CHECK_THROWS_WITH_AS ( j_const [ jp ] = = 1 , throw_msg . c_str ( ) , json : : out_of_range & ) ;
2020-06-20 16:36:28 +03:00
}
2020-03-25 10:56:45 +03:00
2021-08-17 15:43:43 +03:00
DOCTEST_MSVC_SUPPRESS_WARNING_POP
2021-01-26 22:53:02 +03:00
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( j . at ( " /one " _json_pointer ) = 1 ,
" [json.exception.parse_error.109] parse error: array index 'one' is not a number " , json : : parse_error & ) ;
CHECK_THROWS_WITH_AS ( j_const . at ( " /one " _json_pointer ) = = 1 ,
" [json.exception.parse_error.109] parse error: array index 'one' is not a number " , json : : parse_error & ) ;
2017-03-08 18:39:17 +03:00
2020-06-03 22:22:07 +03:00
CHECK ( ! j . contains ( " /one " _json_pointer ) ) ;
CHECK ( ! j . contains ( " /one " _json_pointer ) ) ;
CHECK ( ! j_const . contains ( " /one " _json_pointer ) ) ;
CHECK ( ! j_const . contains ( " /one " _json_pointer ) ) ;
2019-06-30 11:03:08 +03:00
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( json ( { { " /list/0 " , 1 } , { " /list/1 " , 2 } , { " /list/three " , 3 } } ) . unflatten ( ) ,
" [json.exception.parse_error.109] parse error: array index 'three' is not a number " , json : : parse_error & ) ;
2016-08-04 22:55:47 +03:00
// assign to "-"
j [ " /- " _json_pointer ] = 99 ;
CHECK ( j = = json ( { 1 , 13 , 3 , 33 , nullptr , 55 , 99 } ) ) ;
// error when using "-" in const object
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( j_const [ " /- " _json_pointer ] ,
" [json.exception.out_of_range.402] array index '-' (3) is out of range " , json : : out_of_range & ) ;
2020-06-03 22:22:07 +03:00
CHECK ( ! j_const . contains ( " /- " _json_pointer ) ) ;
2016-08-04 22:55:47 +03:00
// error when using "-" with at
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( j . at ( " /- " _json_pointer ) ,
" [json.exception.out_of_range.402] array index '-' (7) is out of range " , json : : out_of_range & ) ;
CHECK_THROWS_WITH_AS ( j_const . at ( " /- " _json_pointer ) ,
" [json.exception.out_of_range.402] array index '-' (3) is out of range " , json : : out_of_range & ) ;
2020-06-03 22:22:07 +03:00
CHECK ( ! j_const . contains ( " /- " _json_pointer ) ) ;
2016-08-04 22:55:47 +03:00
}
SECTION ( " const access " )
{
const json j = { 1 , 2 , 3 } ;
// check reading access
CHECK ( j [ " /0 " _json_pointer ] = = j [ 0 ] ) ;
CHECK ( j [ " /1 " _json_pointer ] = = j [ 1 ] ) ;
CHECK ( j [ " /2 " _json_pointer ] = = j [ 2 ] ) ;
// assign to nonexisting index
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( j . at ( " /3 " _json_pointer ) ,
" [json.exception.out_of_range.401] array index 3 is out of range " , json : : out_of_range & ) ;
2020-06-03 22:22:07 +03:00
CHECK ( ! j . contains ( " /3 " _json_pointer ) ) ;
2016-08-04 22:55:47 +03:00
// assign to nonexisting index (with gap)
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( j . at ( " /5 " _json_pointer ) ,
" [json.exception.out_of_range.401] array index 5 is out of range " , json : : out_of_range & ) ;
2020-06-03 22:22:07 +03:00
CHECK ( ! j . contains ( " /5 " _json_pointer ) ) ;
2016-08-04 22:55:47 +03:00
// assign to "-"
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( j [ " /- " _json_pointer ] ,
" [json.exception.out_of_range.402] array index '-' (3) is out of range " , json : : out_of_range & ) ;
CHECK_THROWS_WITH_AS ( j . at ( " /- " _json_pointer ) ,
" [json.exception.out_of_range.402] array index '-' (3) is out of range " , json : : out_of_range & ) ;
2020-06-03 22:22:07 +03:00
CHECK ( ! j . contains ( " /- " _json_pointer ) ) ;
2016-08-04 22:55:47 +03:00
}
}
SECTION ( " flatten " )
{
json j =
{
{ " pi " , 3.141 } ,
{ " happy " , true } ,
{ " name " , " Niels " } ,
{ " nothing " , nullptr } ,
{
" answer " , {
{ " everything " , 42 }
}
} ,
{ " list " , { 1 , 0 , 2 } } ,
{
" object " , {
{ " currency " , " USD " } ,
{ " value " , 42.99 } ,
{ " " , " empty string " } ,
{ " / " , " slash " } ,
{ " ~ " , " tilde " } ,
{ " ~1 " , " tilde1 " }
}
}
} ;
json j_flatten =
{
{ " /pi " , 3.141 } ,
{ " /happy " , true } ,
{ " /name " , " Niels " } ,
{ " /nothing " , nullptr } ,
{ " /answer/everything " , 42 } ,
{ " /list/0 " , 1 } ,
{ " /list/1 " , 0 } ,
{ " /list/2 " , 2 } ,
{ " /object/currency " , " USD " } ,
{ " /object/value " , 42.99 } ,
{ " /object/ " , " empty string " } ,
{ " /object/~1 " , " slash " } ,
{ " /object/~0 " , " tilde " } ,
{ " /object/~01 " , " tilde1 " }
} ;
// check if flattened result is as expected
CHECK ( j . flatten ( ) = = j_flatten ) ;
// check if unflattened result is as expected
CHECK ( j_flatten . unflatten ( ) = = j ) ;
// error for nonobjects
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( json ( 1 ) . unflatten ( ) ,
" [json.exception.type_error.314] only objects can be unflattened " , json : : type_error & ) ;
2016-08-04 22:55:47 +03:00
// error for nonprimitve values
2021-01-25 15:47:50 +03:00
# if JSON_DIAGNOSTICS
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( json ( { { " /1 " , { 1 , 2 , 3 } } } ) . unflatten ( ) , " [json.exception.type_error.315] (/~11) values in object must be primitive " , json : : type_error & ) ;
2021-01-25 15:47:50 +03:00
# else
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( json ( { { " /1 " , { 1 , 2 , 3 } } } ) . unflatten ( ) , " [json.exception.type_error.315] values in object must be primitive " , json : : type_error & ) ;
2021-01-25 15:47:50 +03:00
# endif
2016-08-04 22:55:47 +03:00
// error for conflicting values
json j_error = { { " " , 42 } , { " /foo " , 17 } } ;
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( j_error . unflatten ( ) ,
" [json.exception.type_error.313] invalid value to unflatten " , json : : type_error & ) ;
2016-08-04 22:55:47 +03:00
// explicit roundtrip check
CHECK ( j . flatten ( ) . unflatten ( ) = = j ) ;
// roundtrip for primitive values
json j_null ;
CHECK ( j_null . flatten ( ) . unflatten ( ) = = j_null ) ;
json j_number = 42 ;
CHECK ( j_number . flatten ( ) . unflatten ( ) = = j_number ) ;
json j_boolean = false ;
CHECK ( j_boolean . flatten ( ) . unflatten ( ) = = j_boolean ) ;
json j_string = " foo " ;
CHECK ( j_string . flatten ( ) . unflatten ( ) = = j_string ) ;
// roundtrip for empty structured values (will be unflattened to null)
json j_array ( json : : value_t : : array ) ;
CHECK ( j_array . flatten ( ) . unflatten ( ) = = json ( ) ) ;
json j_object ( json : : value_t : : object ) ;
CHECK ( j_object . flatten ( ) . unflatten ( ) = = json ( ) ) ;
}
SECTION ( " string representation " )
{
2022-07-28 23:12:23 +03:00
for ( const auto * ptr_str :
2016-08-04 22:55:47 +03:00
{ " " , " /foo " , " /foo/0 " , " / " , " /a~1b " , " /c%d " , " /e^f " , " /g|h " , " /i \\ j " , " /k \" l " , " / " , " /m~0n "
} )
{
2022-07-28 23:12:23 +03:00
json : : json_pointer ptr ( ptr_str ) ;
std : : stringstream ss ;
ss < < ptr ;
CHECK ( ptr . to_string ( ) = = ptr_str ) ;
CHECK ( std : : string ( ptr ) = = ptr_str ) ;
CHECK ( ss . str ( ) = = ptr_str ) ;
2016-08-04 22:55:47 +03:00
}
}
2017-09-14 18:31:28 +03:00
SECTION ( " conversion " )
{
SECTION ( " array " )
{
json j ;
// all numbers -> array
j [ " /12 " _json_pointer ] = 0 ;
CHECK ( j . is_array ( ) ) ;
}
SECTION ( " object " )
{
json j ;
// contains a number, but is not a number -> object
j [ " /a12 " _json_pointer ] = 0 ;
CHECK ( j . is_object ( ) ) ;
}
}
2019-01-15 16:39:06 +03:00
2019-02-25 12:25:02 +03:00
SECTION ( " empty, push, pop and parent " )
2019-01-15 16:39:06 +03:00
{
const json j =
{
{ " " , " Hello " } ,
{ " pi " , 3.141 } ,
{ " happy " , true } ,
{ " name " , " Niels " } ,
{ " nothing " , nullptr } ,
{
" answer " , {
{ " everything " , 42 }
}
} ,
{ " list " , { 1 , 0 , 2 } } ,
{
" object " , {
{ " currency " , " USD " } ,
{ " value " , 42.99 } ,
{ " " , " empty string " } ,
{ " / " , " slash " } ,
{ " ~ " , " tilde " } ,
{ " ~1 " , " tilde1 " }
}
}
} ;
// empty json_pointer returns the root JSON-object
auto ptr = " " _json_pointer ;
2019-02-25 12:25:02 +03:00
CHECK ( ptr . empty ( ) ) ;
2019-01-15 16:39:06 +03:00
CHECK ( j [ ptr ] = = j ) ;
// simple field access
ptr . push_back ( " pi " ) ;
2019-02-25 12:25:02 +03:00
CHECK ( ! ptr . empty ( ) ) ;
2019-01-15 16:39:06 +03:00
CHECK ( j [ ptr ] = = j [ " pi " ] ) ;
ptr . pop_back ( ) ;
2019-02-25 12:25:02 +03:00
CHECK ( ptr . empty ( ) ) ;
2019-01-15 16:39:06 +03:00
CHECK ( j [ ptr ] = = j ) ;
// object and children access
2019-01-31 22:15:36 +03:00
const std : : string answer ( " answer " ) ;
ptr . push_back ( answer ) ;
2019-01-15 16:39:06 +03:00
ptr . push_back ( " everything " ) ;
2019-02-25 12:25:02 +03:00
CHECK ( ! ptr . empty ( ) ) ;
2019-01-15 16:39:06 +03:00
CHECK ( j [ ptr ] = = j [ " answer " ] [ " everything " ] ) ;
2019-10-01 01:57:27 +03:00
// check access via const pointer
const auto cptr = ptr ;
CHECK ( cptr . back ( ) = = " everything " ) ;
2019-01-15 16:39:06 +03:00
ptr . pop_back ( ) ;
ptr . pop_back ( ) ;
2019-02-25 12:25:02 +03:00
CHECK ( ptr . empty ( ) ) ;
2019-01-15 16:39:06 +03:00
CHECK ( j [ ptr ] = = j ) ;
// push key which has to be encoded
ptr . push_back ( " object " ) ;
ptr . push_back ( " / " ) ;
CHECK ( j [ ptr ] = = j [ " object " ] [ " / " ] ) ;
CHECK ( ptr . to_string ( ) = = " /object/~1 " ) ;
2019-02-25 12:25:02 +03:00
CHECK ( j [ ptr . parent_pointer ( ) ] = = j [ " object " ] ) ;
ptr = ptr . parent_pointer ( ) . parent_pointer ( ) ;
CHECK ( ptr . empty ( ) ) ;
CHECK ( j [ ptr ] = = j ) ;
// parent-pointer of the empty json_pointer is empty
ptr = ptr . parent_pointer ( ) ;
CHECK ( ptr . empty ( ) ) ;
CHECK ( j [ ptr ] = = j ) ;
CHECK_THROWS_WITH ( ptr . pop_back ( ) ,
" [json.exception.out_of_range.405] JSON pointer has no parent " ) ;
2019-01-15 16:39:06 +03:00
}
2019-01-24 18:46:51 +03:00
SECTION ( " operators " )
{
const json j =
{
{ " " , " Hello " } ,
{ " pi " , 3.141 } ,
{ " happy " , true } ,
{ " name " , " Niels " } ,
{ " nothing " , nullptr } ,
{
" answer " , {
{ " everything " , 42 }
}
} ,
{ " list " , { 1 , 0 , 2 } } ,
{
" object " , {
{ " currency " , " USD " } ,
{ " value " , 42.99 } ,
{ " " , " empty string " } ,
{ " / " , " slash " } ,
{ " ~ " , " tilde " } ,
{ " ~1 " , " tilde1 " }
}
}
} ;
// empty json_pointer returns the root JSON-object
auto ptr = " " _json_pointer ;
CHECK ( j [ ptr ] = = j ) ;
// simple field access
2019-01-31 22:15:36 +03:00
ptr = ptr / " pi " ;
2019-01-24 18:46:51 +03:00
CHECK ( j [ ptr ] = = j [ " pi " ] ) ;
ptr . pop_back ( ) ;
CHECK ( j [ ptr ] = = j ) ;
// object and children access
2019-01-31 22:15:36 +03:00
const std : : string answer ( " answer " ) ;
ptr / = answer ;
ptr = ptr / " everything " ;
2019-01-24 18:46:51 +03:00
CHECK ( j [ ptr ] = = j [ " answer " ] [ " everything " ] ) ;
ptr . pop_back ( ) ;
ptr . pop_back ( ) ;
CHECK ( j [ ptr ] = = j ) ;
2019-01-31 22:15:36 +03:00
CHECK ( ptr / " " _json_pointer = = ptr ) ;
CHECK ( j [ " /answer " _json_pointer / " /everything " _json_pointer ] = = j [ " answer " ] [ " everything " ] ) ;
// list children access
CHECK ( j [ " /list " _json_pointer / 1 ] = = j [ " list " ] [ 1 ] ) ;
2019-01-24 18:46:51 +03:00
// push key which has to be encoded
2019-01-31 22:15:36 +03:00
ptr / = " object " ;
ptr = ptr / " / " ;
2019-01-24 18:46:51 +03:00
CHECK ( j [ ptr ] = = j [ " object " ] [ " / " ] ) ;
CHECK ( ptr . to_string ( ) = = " /object/~1 " ) ;
}
2022-04-12 15:18:16 +03:00
SECTION ( " equality comparison " )
{
auto ptr1 = json : : json_pointer ( " /foo/bar " ) ;
auto ptr2 = json : : json_pointer ( " /foo/bar " ) ;
CHECK ( ptr1 = = ptr2 ) ;
CHECK_FALSE ( ptr1 ! = ptr2 ) ;
}
SECTION ( " backwards compatibility and mixing " )
{
json j = R " (
{
" foo " : [ " bar " , " baz " ]
}
) " _json;
using nlohmann : : ordered_json ;
using json_ptr_str = nlohmann : : json_pointer < std : : string > ;
using json_ptr_j = nlohmann : : json_pointer < json > ;
using json_ptr_oj = nlohmann : : json_pointer < ordered_json > ;
CHECK ( std : : is_same < json_ptr_str : : string_t , json : : json_pointer : : string_t > : : value ) ;
CHECK ( std : : is_same < json_ptr_str : : string_t , ordered_json : : json_pointer : : string_t > : : value ) ;
CHECK ( std : : is_same < json_ptr_str : : string_t , json_ptr_j : : string_t > : : value ) ;
CHECK ( std : : is_same < json_ptr_str : : string_t , json_ptr_oj : : string_t > : : value ) ;
json_ptr_str ptr { " /foo/0 " } ;
json_ptr_j ptr_j { " /foo/0 " } ;
json_ptr_oj ptr_oj { " /foo/0 " } ;
CHECK ( j . contains ( ptr ) ) ;
CHECK ( j . contains ( ptr_j ) ) ;
CHECK ( j . contains ( ptr_oj ) ) ;
CHECK ( j . at ( ptr ) = = j . at ( ptr_j ) ) ;
CHECK ( j . at ( ptr ) = = j . at ( ptr_oj ) ) ;
CHECK ( j [ ptr ] = = j [ ptr_j ] ) ;
CHECK ( j [ ptr ] = = j [ ptr_oj ] ) ;
CHECK ( j . value ( ptr , " x " ) = = j . value ( ptr_j , " x " ) ) ;
CHECK ( j . value ( ptr , " x " ) = = j . value ( ptr_oj , " x " ) ) ;
CHECK ( ptr = = ptr_j ) ;
CHECK ( ptr = = ptr_oj ) ;
CHECK_FALSE ( ptr ! = ptr_j ) ;
CHECK_FALSE ( ptr ! = ptr_oj ) ;
}
2016-08-04 22:55:47 +03:00
}