Re-template json_pointer on string type (#3415)

* Make exception context optional

Change exception context parameter to pointer and replace context with
nullptr where appropriate.

* Support escaping other string types

* Add string concatenation function

Add variadic concat() function for concatenating char *, char, and
string types.

* Replace string concatenations using + with concat()

* Template json_pointer on string type

Change json_pointer from being templated on basic_json to being
templated on string type.

* Add unit test for #3388

Closes #3388.

* Fix regression test for #2958

* Add backwards compatibility with json_pointer<basic_json>

* Update json_pointer docs

* Allow comparing different json_pointers

* Update version numbers
pull/3439/head
Florian Albrechtskirchinger 2022-04-12 14:18:16 +02:00 committed by GitHub
parent 1deeb434c6
commit 616caea27a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 1638 additions and 784 deletions

View File

@ -1,7 +1,7 @@
# <small>nlohmann::json_pointer::</small>back
```cpp
const std::string& back() const;
const string_t& back() const;
```
Return last reference token.
@ -36,4 +36,5 @@ Constant.
## Version history
Added in version 3.6.0.
- Added in version 3.6.0.
- Changed return type to `string_t` in version 3.11.0.

View File

@ -1,7 +1,7 @@
# <small>nlohmann::</small>json_pointer
```cpp
template<typename BasicJsonType>
template<typename RefStringType>
class json_pointer;
```
@ -11,14 +11,18 @@ are the base for JSON patches.
## Template parameters
`BasicJsonType`
: a specialization of [`basic_json`](../basic_json/index.md)
`RefStringType`
: the string type used for the reference tokens making up the JSON pointer
## Notes
For backwards compatibility `RefStringType` may also be a specialization of [`basic_json`](../basic_json/index.md) in which case `string_t` will be deduced as [`basic_json::string_t`](../basic_json/string_t.md). This feature is deprecated and may be removed in a future major version.
## Member functions
- [(constructor)](json_pointer.md)
- [**to_string**](to_string.md) - return a string representation of the JSON pointer
- [**operator std::string**](operator_string.md) - return a string representation of the JSON pointer
- [**operator string_t**](operator_string.md) - return a string representation of the JSON pointer
- [**operator/=**](operator_slasheq.md) - append to the end of the JSON pointer
- [**operator/**](operator_slash.md) - create JSON Pointer by appending
- [**parent_pointer**](parent_pointer.md) - returns the parent of this JSON pointer
@ -27,6 +31,10 @@ are the base for JSON patches.
- [**push_back**](push_back.md) - append an unescaped token at the end of the pointer
- [**empty**](empty.md) - return whether pointer points to the root document
## Member types
- [**string_t**](string_t.md) - the string type used for the reference tokens
## See also
- [operator""_json_pointer](../basic_json/operator_literal_json_pointer.md) - user-defined string literal for JSON pointers
@ -34,4 +42,5 @@ are the base for JSON patches.
## Version history
Added in version 2.0.0.
- Added in version 2.0.0.
- Changed template parameter from `basic_json` to string type in version 3.11.0.

View File

@ -1,7 +1,7 @@
# <small>nlohmann::json_pointer::</small>json_pointer
```cpp
explicit json_pointer(const std::string& s = "");
explicit json_pointer(const string_t& s = "");
```
Create a JSON pointer according to the syntax described in
@ -37,4 +37,5 @@ Create a JSON pointer according to the syntax described in
## Version history
Added in version 2.0.0.
- Added in version 2.0.0.
- Changed type of `s` to `string_t` in version 3.11.0.

View File

@ -5,7 +5,7 @@
json_pointer operator/(const json_pointer& lhs, const json_pointer& rhs);
// (2)
json_pointer operator/(const json_pointer& lhs, std::string token);
json_pointer operator/(const json_pointer& lhs, string_t token);
// (3)
json_pointer operator/(const json_pointer& lhs, std::size_t array_idx);
@ -60,5 +60,5 @@ json_pointer operator/(const json_pointer& lhs, std::size_t array_idx);
## Version history
1. Added in version 3.6.0.
2. Added in version 3.6.0.
2. Added in version 3.6.0. Changed type of `token` to `string_t` in version 3.11.0.
3. Added in version 3.6.0.

View File

@ -5,7 +5,7 @@
json_pointer& operator/=(const json_pointer& ptr);
// (2)
json_pointer& operator/=(std::string token);
json_pointer& operator/=(string_t token);
// (3)
json_pointer& operator/=(std::size_t array_idx)
@ -57,5 +57,5 @@ json_pointer& operator/=(std::size_t array_idx)
## Version history
1. Added in version 3.6.0.
2. Added in version 3.6.0.
2. Added in version 3.6.0. Changed type of `token` to `string_t` in version 3.11.0.
3. Added in version 3.6.0.

View File

@ -1,7 +1,7 @@
# <small>nlohmann::json_pointer::</small>operator std::string
# <small>nlohmann::json_pointer::</small>operator string_t
```cpp
operator std::string() const
operator string_t() const
```
Return a string representation of the JSON pointer.
@ -13,7 +13,7 @@ A string representation of the JSON pointer
## Possible implementation
```cpp
operator std::string() const
operator string_t() const
{
return to_string();
}
@ -21,4 +21,5 @@ operator std::string() const
## Version history
Since version 2.0.0.
- Since version 2.0.0.
- Changed type to `string_t` in version 3.11.0.

View File

@ -1,9 +1,9 @@
# <small>nlohmann::json_pointer::</small>push_back
```cpp
void push_back(const std::string& token);
void push_back(const string_t& token);
void push_back(std::string&& token);
void push_back(string_t&& token);
```
Append an unescaped token at the end of the reference pointer.
@ -35,4 +35,5 @@ Amortized constant.
## Version history
Added in version 3.6.0.
- Added in version 3.6.0.
- Changed type of `token` to `string_t` in version 3.11.0.

View File

@ -0,0 +1,12 @@
# <small>nlohmann::json_pointer::</small>string_t
```cpp
using string_t = RefStringType;
```
The string type used for the reference tokens making up the JSON pointer.
See [`basic_json::string_t`](../basic_json/string_t.md) for more information.
## Version history
- Added in version 3.11.0.

View File

@ -1,7 +1,7 @@
# <small>nlohmann::json_pointer::</small>to_string
```cpp
std::string to_string() const;
string_t to_string() const;
```
Return a string representation of the JSON pointer.
@ -36,4 +36,5 @@ ptr == json_pointer(ptr.to_string());
## Version history
Since version 2.0.0.
- Since version 2.0.0.
- Changed return type to `string_t` in version 3.11.0.

View File

@ -211,6 +211,7 @@ nav:
- 'parent_pointer': api/json_pointer/parent_pointer.md
- 'pop_back': api/json_pointer/pop_back.md
- 'push_back': api/json_pointer/push_back.md
- 'string_t': api/json_pointer/string_t.md
- 'to_string': api/json_pointer/to_string.md
- json_sax:
- 'Overview': api/json_sax/index.md

View File

@ -17,6 +17,7 @@
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/identity_tag.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/string_concat.hpp>
#include <nlohmann/detail/value_t.hpp>
#if JSON_HAS_EXPERIMENTAL_FILESYSTEM
@ -42,7 +43,7 @@ void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
{
JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()), j));
JSON_THROW(type_error::create(302, concat("type must be null, but is ", j.type_name()), &j));
}
n = nullptr;
}
@ -80,7 +81,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
case value_t::binary:
case value_t::discarded:
default:
JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j));
JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j));
}
}
@ -89,7 +90,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
{
JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()), j));
JSON_THROW(type_error::create(302, concat("type must be boolean, but is ", j.type_name()), &j));
}
b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
}
@ -99,7 +100,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{
JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j));
JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
}
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
}
@ -114,7 +115,7 @@ void from_json(const BasicJsonType& j, StringType& s)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{
JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j));
JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
}
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
@ -154,7 +155,7 @@ void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
}
l.clear();
std::transform(j.rbegin(), j.rend(),
@ -171,7 +172,7 @@ void from_json(const BasicJsonType& j, std::valarray<T>& l)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
}
l.resize(j.size());
std::transform(j.begin(), j.end(), std::begin(l),
@ -268,7 +269,7 @@ void())
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
}
from_json_array_impl(j, arr, priority_tag<3> {});
@ -287,7 +288,7 @@ auto from_json(BasicJsonType&& j, identity_tag<std::array<T, N>> tag)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
}
return from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {});
@ -298,7 +299,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
{
JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()), j));
JSON_THROW(type_error::create(302, concat("type must be binary, but is ", j.type_name()), &j));
}
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
@ -310,7 +311,7 @@ void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
{
JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()), j));
JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j));
}
ConstructibleObjectType ret;
@ -370,7 +371,7 @@ void from_json(const BasicJsonType& j, ArithmeticType& val)
case value_t::binary:
case value_t::discarded:
default:
JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j));
JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j));
}
}
@ -411,7 +412,7 @@ auto from_json(BasicJsonType&& j, TupleRelated&& t)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
}
return from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {});
@ -424,14 +425,14 @@ void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>&
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
}
m.clear();
for (const auto& p : j)
{
if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j));
JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &j));
}
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
}
@ -444,14 +445,14 @@ void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyE
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
}
m.clear();
for (const auto& p : j)
{
if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j));
JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &j));
}
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
}
@ -463,7 +464,7 @@ void from_json(const BasicJsonType& j, std_fs::path& p)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{
JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j));
JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
}
p = *j.template get_ptr<const typename BasicJsonType::string_t*>();
}

View File

@ -1,5 +1,6 @@
#pragma once
#include <cstddef> // nullptr_t
#include <exception> // exception
#include <stdexcept> // runtime_error
#include <string> // to_string
@ -9,6 +10,10 @@
#include <nlohmann/detail/string_escape.hpp>
#include <nlohmann/detail/input/position_t.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/string_concat.hpp>
namespace nlohmann
{
@ -38,15 +43,20 @@ class exception : public std::exception
static std::string name(const std::string& ename, int id_)
{
return "[json.exception." + ename + "." + std::to_string(id_) + "] ";
return concat("[json.exception.", ename, '.', std::to_string(id_), "] ");
}
static std::string diagnostics(std::nullptr_t /*leaf_element*/)
{
return "";
}
template<typename BasicJsonType>
static std::string diagnostics(const BasicJsonType& leaf_element)
static std::string diagnostics(const BasicJsonType* leaf_element)
{
#if JSON_DIAGNOSTICS
std::vector<std::string> tokens;
for (const auto* current = &leaf_element; current->m_parent != nullptr; current = current->m_parent)
for (const auto* current = leaf_element; current != nullptr && current->m_parent != nullptr; current = current->m_parent)
{
switch (current->m_parent->type())
{
@ -94,11 +104,12 @@ class exception : public std::exception
return "";
}
return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},
[](const std::string & a, const std::string & b)
auto str = std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},
[](const std::string & a, const std::string & b)
{
return a + "/" + detail::escape(b);
}) + ") ";
return concat(a, '/', detail::escape(b));
});
return concat('(', str, ") ");
#else
static_cast<void>(leaf_element);
return "";
@ -124,20 +135,20 @@ class parse_error : public exception
@param[in] what_arg the explanatory string
@return parse_error object
*/
template<typename BasicJsonType>
static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const BasicJsonType& context)
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static parse_error create(int id_, const position_t& pos, const std::string& what_arg, BasicJsonContext context)
{
std::string w = exception::name("parse_error", id_) + "parse error" +
position_string(pos) + ": " + exception::diagnostics(context) + what_arg;
std::string w = concat(exception::name("parse_error", id_), "parse error",
position_string(pos), ": ", exception::diagnostics(context), what_arg);
return {id_, pos.chars_read_total, w.c_str()};
}
template<typename BasicJsonType>
static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const BasicJsonType& context)
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, BasicJsonContext context)
{
std::string w = exception::name("parse_error", id_) + "parse error" +
(byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") +
": " + exception::diagnostics(context) + what_arg;
std::string w = concat(exception::name("parse_error", id_), "parse error",
(byte_ != 0 ? (concat(" at byte ", std::to_string(byte_))) : ""),
": ", exception::diagnostics(context), what_arg);
return {id_, byte_, w.c_str()};
}
@ -158,8 +169,8 @@ class parse_error : public exception
static std::string position_string(const position_t& pos)
{
return " at line " + std::to_string(pos.lines_read + 1) +
", column " + std::to_string(pos.chars_read_current_line);
return concat(" at line ", std::to_string(pos.lines_read + 1),
", column ", std::to_string(pos.chars_read_current_line));
}
};
@ -168,10 +179,10 @@ class parse_error : public exception
class invalid_iterator : public exception
{
public:
template<typename BasicJsonType>
static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context)
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static invalid_iterator create(int id_, const std::string& what_arg, BasicJsonContext context)
{
std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg;
std::string w = concat(exception::name("invalid_iterator", id_), exception::diagnostics(context), what_arg);
return {id_, w.c_str()};
}
@ -186,10 +197,10 @@ class invalid_iterator : public exception
class type_error : public exception
{
public:
template<typename BasicJsonType>
static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context)
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static type_error create(int id_, const std::string& what_arg, BasicJsonContext context)
{
std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg;
std::string w = concat(exception::name("type_error", id_), exception::diagnostics(context), what_arg);
return {id_, w.c_str()};
}
@ -203,10 +214,10 @@ class type_error : public exception
class out_of_range : public exception
{
public:
template<typename BasicJsonType>
static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context)
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static out_of_range create(int id_, const std::string& what_arg, BasicJsonContext context)
{
std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg;
std::string w = concat(exception::name("out_of_range", id_), exception::diagnostics(context), what_arg);
return {id_, w.c_str()};
}
@ -220,10 +231,10 @@ class out_of_range : public exception
class other_error : public exception
{
public:
template<typename BasicJsonType>
static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context)
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static other_error create(int id_, const std::string& what_arg, BasicJsonContext context)
{
std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg;
std::string w = concat(exception::name("other_error", id_), exception::diagnostics(context), what_arg);
return {id_, w.c_str()};
}

View File

@ -20,6 +20,7 @@
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta/is_sax.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/string_concat.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
@ -139,8 +140,8 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(current != std::char_traits<char_type>::eof()))
{
return sax->parse_error(chars_read, get_token_string(),
parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"), BasicJsonType()));
return sax->parse_error(chars_read, get_token_string(), parse_error::create(110, chars_read,
exception_message(format, concat("expected end of input; last byte: 0x", get_token_string()), "value"), nullptr));
}
}
@ -216,7 +217,8 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(len < 1))
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"), BasicJsonType()));
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(input_format_t::bson, concat("string length must be at least 1, is ", std::to_string(len)), "string"), nullptr));
}
return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) && get() != std::char_traits<char_type>::eof();
@ -237,7 +239,8 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(len < 0))
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"), BasicJsonType()));
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(input_format_t::bson, concat("byte array length cannot be negative, is ", std::to_string(len)), "binary"), nullptr));
}
// All BSON binary values have a subtype
@ -319,7 +322,9 @@ class binary_reader
{
std::array<char, 3> cr{{}};
static_cast<void>((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(element_type))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()), BasicJsonType()));
std::string cr_str{cr.data()};
return sax->parse_error(element_type_parse_position, cr_str,
parse_error::create(114, element_type_parse_position, concat("Unsupported BSON record type 0x", cr_str), nullptr));
}
}
}
@ -719,7 +724,8 @@ class binary_reader
case cbor_tag_handler_t::error:
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType()));
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(input_format_t::cbor, concat("invalid byte: 0x", last_token), "value"), nullptr));
}
case cbor_tag_handler_t::ignore:
@ -876,7 +882,8 @@ class binary_reader
default: // anything else (0xFF is handled inside the other types)
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType()));
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(input_format_t::cbor, concat("invalid byte: 0x", last_token), "value"), nullptr));
}
}
}
@ -971,7 +978,8 @@ class binary_reader
default:
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"), BasicJsonType()));
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(input_format_t::cbor, concat("expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x", last_token), "string"), nullptr));
}
}
}
@ -1070,7 +1078,8 @@ class binary_reader
default:
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"), BasicJsonType()));
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(input_format_t::cbor, concat("expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x", last_token), "binary"), nullptr));
}
}
}
@ -1540,7 +1549,8 @@ class binary_reader
default: // anything else
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"), BasicJsonType()));
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(input_format_t::msgpack, concat("invalid byte: 0x", last_token), "value"), nullptr));
}
}
}
@ -1622,7 +1632,8 @@ class binary_reader
default:
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"), BasicJsonType()));
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(input_format_t::msgpack, concat("expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x", last_token), "string"), nullptr));
}
}
}
@ -1872,7 +1883,8 @@ class binary_reader
default:
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"), BasicJsonType()));
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(input_format_t::ubjson, concat("expected length type specification (U, i, I, l, L); last byte: 0x", last_token), "string"), nullptr));
}
}
@ -1942,7 +1954,8 @@ class binary_reader
default:
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"), BasicJsonType()));
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(input_format_t::ubjson, concat("expected length type specification (U, i, I, l, L) after '#'; last byte: 0x", last_token), "size"), nullptr));
}
}
}
@ -1980,7 +1993,8 @@ class binary_reader
return false;
}
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"), BasicJsonType()));
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(input_format_t::ubjson, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
}
return get_ubjson_size_value(result.first);
@ -2070,7 +2084,8 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(current > 127))
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"), BasicJsonType()));
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(input_format_t::ubjson, concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", last_token), "char"), nullptr));
}
string_t s(1, static_cast<typename string_t::value_type>(current));
return sax->string(s);
@ -2091,7 +2106,8 @@ class binary_reader
default: // anything else
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"), BasicJsonType()));
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
exception_message(input_format_t::ubjson, concat("invalid byte: 0x", last_token), "value"), nullptr));
}
}
}
@ -2269,7 +2285,8 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input))
{
return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType()));
return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
exception_message(input_format_t::ubjson, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
}
switch (result_number)
@ -2295,7 +2312,8 @@ class binary_reader
case token_type::end_of_input:
case token_type::literal_or_value:
default:
return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType()));
return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
exception_message(input_format_t::ubjson, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
}
}
@ -2451,7 +2469,7 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(current == std::char_traits<char_type>::eof()))
{
return sax->parse_error(chars_read, "<end of file>",
parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), BasicJsonType()));
parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), nullptr));
}
return true;
}
@ -2501,7 +2519,7 @@ class binary_reader
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
return error_msg + " " + context + ": " + detail;
return concat(error_msg, ' ', context, ": ", detail);
}
private:

View File

@ -7,6 +7,7 @@
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/string_concat.hpp>
namespace nlohmann
{
@ -224,7 +225,7 @@ class json_sax_dom_parser
if (JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
{
JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back()));
JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
}
return true;
@ -250,7 +251,7 @@ class json_sax_dom_parser
if (JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
{
JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back()));
JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
}
return true;
@ -405,7 +406,7 @@ class json_sax_dom_callback_parser
// check object limit
if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
{
JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back()));
JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
}
return true;
@ -475,7 +476,7 @@ class json_sax_dom_callback_parser
// check array limit
if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
{
JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back()));
JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
}
return true;

View File

@ -13,6 +13,7 @@
#include <nlohmann/detail/input/lexer.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta/is_sax.hpp>
#include <nlohmann/detail/string_concat.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
@ -95,7 +96,7 @@ class parser
sdp.parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::end_of_input, "value"), BasicJsonType()));
exception_message(token_type::end_of_input, "value"), nullptr));
}
// in case of an error, return discarded value
@ -122,7 +123,7 @@ class parser
{
sdp.parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType()));
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
}
// in case of an error, return discarded value
@ -160,7 +161,7 @@ class parser
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType()));
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
}
return result;
@ -206,7 +207,7 @@ class parser
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType()));
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
}
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
{
@ -218,7 +219,7 @@ class parser
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType()));
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
}
// remember we are now inside an object
@ -261,7 +262,7 @@ class parser
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'", BasicJsonType()));
out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr));
}
if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
@ -331,7 +332,7 @@ class parser
// using "uninitialized" to avoid "expected" message
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), BasicJsonType()));
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr));
}
case token_type::uninitialized:
@ -345,7 +346,7 @@ class parser
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), BasicJsonType()));
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
}
}
}
@ -391,7 +392,7 @@ class parser
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), BasicJsonType()));
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), nullptr));
}
// states.back() is false -> object
@ -404,7 +405,7 @@ class parser
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType()));
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
}
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
@ -417,7 +418,7 @@ class parser
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType()));
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
}
// parse values
@ -445,7 +446,7 @@ class parser
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), BasicJsonType()));
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), nullptr));
}
}
@ -461,24 +462,24 @@ class parser
if (!context.empty())
{
error_msg += "while parsing " + context + " ";
error_msg += concat("while parsing ", context, ' ');
}
error_msg += "- ";
if (last_token == token_type::parse_error)
{
error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" +
m_lexer.get_token_string() + "'";
error_msg += concat(m_lexer.get_error_message(), "; last read: '",
m_lexer.get_token_string(), '\'');
}
else
{
error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token));
error_msg += concat("unexpected ", lexer_t::token_type_name(last_token));
}
if (expected != token_type::uninitialized)
{
error_msg += "; expected " + std::string(lexer_t::token_type_name(expected));
error_msg += concat("; expected ", lexer_t::token_type_name(expected));
}
return error_msg;

View File

@ -285,7 +285,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
}
case value_t::null:
JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object));
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
case value_t::string:
case value_t::boolean:
@ -301,7 +301,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
return *m_object;
}
JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object));
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
}
}
}
@ -343,7 +343,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
return m_object;
}
JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object));
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
}
}
}
@ -460,7 +460,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
// if objects are not the same, the comparison is undefined
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
{
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object));
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object));
}
JSON_ASSERT(m_object != nullptr);
@ -505,7 +505,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
// if objects are not the same, the comparison is undefined
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
{
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object));
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object));
}
JSON_ASSERT(m_object != nullptr);
@ -513,7 +513,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
switch (m_object->m_type)
{
case value_t::object:
JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", *m_object));
JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", m_object));
case value_t::array:
return (m_it.array_iterator < other.m_it.array_iterator);
@ -569,7 +569,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
switch (m_object->m_type)
{
case value_t::object:
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object));
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object));
case value_t::array:
{
@ -648,7 +648,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
switch (m_object->m_type)
{
case value_t::object:
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object));
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object));
case value_t::array:
return m_it.array_iterator - other.m_it.array_iterator;
@ -677,13 +677,13 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
switch (m_object->m_type)
{
case value_t::object:
JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", *m_object));
JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", m_object));
case value_t::array:
return *std::next(m_it.array_iterator, n);
case value_t::null:
JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object));
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
case value_t::string:
case value_t::boolean:
@ -699,7 +699,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
return *m_object;
}
JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object));
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
}
}
}
@ -717,7 +717,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
return m_it.object_iterator->first;
}
JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", *m_object));
JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", m_object));
}
/*!

View File

@ -2,6 +2,8 @@
#include <algorithm> // all_of
#include <cctype> // isdigit
#include <cerrno> // errno, ERANGE
#include <cstdlib> // strtoull
#include <limits> // max
#include <numeric> // accumulate
#include <string> // string
@ -10,6 +12,7 @@
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/string_concat.hpp>
#include <nlohmann/detail/string_escape.hpp>
#include <nlohmann/detail/value_t.hpp>
@ -18,35 +21,53 @@ namespace nlohmann
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
/// @sa https://json.nlohmann.me/api/json_pointer/
template<typename BasicJsonType>
template<typename RefStringType>
class json_pointer
{
// allow basic_json to access private members
NLOHMANN_BASIC_JSON_TPL_DECLARATION
friend class basic_json;
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;
};
public:
// for backwards compatibility accept BasicJsonType
using string_t = typename string_t_helper<RefStringType>::type;
/// @brief create JSON pointer
/// @sa https://json.nlohmann.me/api/json_pointer/json_pointer/
explicit json_pointer(const std::string& s = "")
explicit json_pointer(const string_t& s = "")
: reference_tokens(split(s))
{}
/// @brief return a string representation of the JSON pointer
/// @sa https://json.nlohmann.me/api/json_pointer/to_string/
std::string to_string() const
string_t to_string() const
{
return std::accumulate(reference_tokens.begin(), reference_tokens.end(),
std::string{},
[](const std::string & a, const std::string & b)
string_t{},
[](const string_t& a, const string_t& b)
{
return a + "/" + detail::escape(b);
return detail::concat(a, '/', detail::escape(b));
});
}
/// @brief return a string representation of the JSON pointer
/// @sa https://json.nlohmann.me/api/json_pointer/operator_string/
operator std::string() const
operator string_t() const
{
return to_string();
}
@ -63,7 +84,7 @@ class json_pointer
/// @brief append an unescaped reference token at the end of this JSON pointer
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
json_pointer& operator/=(std::string token)
json_pointer& operator/=(string_t token)
{
push_back(std::move(token));
return *this;
@ -86,7 +107,7 @@ class json_pointer
/// @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/
friend json_pointer operator/(const json_pointer& lhs, std::string token) // NOLINT(performance-unnecessary-value-param)
friend json_pointer operator/(const json_pointer& lhs, string_t token) // NOLINT(performance-unnecessary-value-param)
{
return json_pointer(lhs) /= std::move(token);
}
@ -118,7 +139,7 @@ class json_pointer
{
if (JSON_HEDLEY_UNLIKELY(empty()))
{
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType()));
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
}
reference_tokens.pop_back();
@ -126,11 +147,11 @@ class json_pointer
/// @brief return last reference token
/// @sa https://json.nlohmann.me/api/json_pointer/back/
const std::string& back() const
const string_t& back() const
{
if (JSON_HEDLEY_UNLIKELY(empty()))
{
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType()));
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
}
return reference_tokens.back();
@ -138,14 +159,14 @@ class json_pointer
/// @brief append an unescaped token at the end of the reference pointer
/// @sa https://json.nlohmann.me/api/json_pointer/push_back/
void push_back(const std::string& token)
void push_back(const string_t& token)
{
reference_tokens.push_back(token);
}
/// @brief append an unescaped token at the end of the reference pointer
/// @sa https://json.nlohmann.me/api/json_pointer/push_back/
void push_back(std::string&& token)
void push_back(string_t&& token)
{
reference_tokens.push_back(std::move(token));
}
@ -168,44 +189,39 @@ class json_pointer
@throw out_of_range.404 if string @a s could not be converted to an integer
@throw out_of_range.410 if an array index exceeds size_type
*/
static typename BasicJsonType::size_type array_index(const std::string& s)
template<typename BasicJsonType>
static typename BasicJsonType::size_type array_index(const string_t& s)
{
using size_type = typename BasicJsonType::size_type;
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0'))
{
JSON_THROW(detail::parse_error::create(106, 0, "array index '" + s + "' must not begin with '0'", BasicJsonType()));
JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr));
}
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9')))
{
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number", BasicJsonType()));
JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", s, "' is not a number"), nullptr));
}
std::size_t processed_chars = 0;
unsigned long long res = 0; // NOLINT(runtime/int)
JSON_TRY
const char* p = s.c_str();
char* p_end = nullptr;
errno = 0; // strtoull doesn't reset errno
unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int)
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
{
res = std::stoull(s, &processed_chars);
}
JSON_CATCH(std::out_of_range&)
{
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType()));
}
// check if the string was completely read
if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size()))
{
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType()));
JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", s, "'"), nullptr));
}
// only triggered on special platforms (like 32bit), see also
// https://github.com/nlohmann/json/pull/2203
if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)())) // NOLINT(runtime/int)
{
JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type", BasicJsonType())); // LCOV_EXCL_LINE
JSON_THROW(detail::out_of_range::create(410, detail::concat("array index ", s, " exceeds size_type"), nullptr)); // LCOV_EXCL_LINE
}
return static_cast<size_type>(res);
@ -216,7 +232,7 @@ class json_pointer
{
if (JSON_HEDLEY_UNLIKELY(empty()))
{
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType()));
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
}
json_pointer result = *this;
@ -233,6 +249,7 @@ class json_pointer
@throw parse_error.109 if array index is not a number
@throw type_error.313 if value cannot be unflattened
*/
template<typename BasicJsonType>
BasicJsonType& get_and_create(BasicJsonType& j) const
{
auto* result = &j;
@ -268,7 +285,7 @@ class json_pointer
case detail::value_t::array:
{
// create an entry in the array
result = &result->operator[](array_index(reference_token));
result = &result->operator[](array_index<BasicJsonType>(reference_token));
break;
}
@ -286,7 +303,7 @@ class json_pointer
case detail::value_t::binary:
case detail::value_t::discarded:
default:
JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", j));
JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", &j));
}
}
@ -312,6 +329,7 @@ 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
*/
template<typename BasicJsonType>
BasicJsonType& get_unchecked(BasicJsonType* ptr) const
{
for (const auto& reference_token : reference_tokens)
@ -352,7 +370,7 @@ class json_pointer
else
{
// convert array index to number; unchecked access
ptr = &ptr->operator[](array_index(reference_token));
ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
}
break;
}
@ -366,7 +384,7 @@ class json_pointer
case detail::value_t::binary:
case detail::value_t::discarded:
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr));
JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
}
}
@ -379,6 +397,7 @@ 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
*/
template<typename BasicJsonType>
BasicJsonType& get_checked(BasicJsonType* ptr) const
{
for (const auto& reference_token : reference_tokens)
@ -397,13 +416,13 @@ class json_pointer
if (JSON_HEDLEY_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", *ptr));
JSON_THROW(detail::out_of_range::create(402, detail::concat(
"array index '-' (", std::to_string(ptr->m_value.array->size()),
") is out of range"), ptr));
}
// note: at performs range check
ptr = &ptr->at(array_index(reference_token));
ptr = &ptr->at(array_index<BasicJsonType>(reference_token));
break;
}
@ -416,7 +435,7 @@ class json_pointer
case detail::value_t::binary:
case detail::value_t::discarded:
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr));
JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
}
}
@ -436,6 +455,7 @@ 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
*/
template<typename BasicJsonType>
const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const
{
for (const auto& reference_token : reference_tokens)
@ -454,11 +474,11 @@ class json_pointer
if (JSON_HEDLEY_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", *ptr));
JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_value.array->size()), ") is out of range"), ptr));
}
// use unchecked array access
ptr = &ptr->operator[](array_index(reference_token));
ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
break;
}
@ -471,7 +491,7 @@ class json_pointer
case detail::value_t::binary:
case detail::value_t::discarded:
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr));
JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
}
}
@ -484,6 +504,7 @@ 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
*/
template<typename BasicJsonType>
const BasicJsonType& get_checked(const BasicJsonType* ptr) const
{
for (const auto& reference_token : reference_tokens)
@ -502,13 +523,13 @@ class json_pointer
if (JSON_HEDLEY_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", *ptr));
JSON_THROW(detail::out_of_range::create(402, detail::concat(
"array index '-' (", std::to_string(ptr->m_value.array->size()),
") is out of range"), ptr));
}
// note: at performs range check
ptr = &ptr->at(array_index(reference_token));
ptr = &ptr->at(array_index<BasicJsonType>(reference_token));
break;
}
@ -521,7 +542,7 @@ class json_pointer
case detail::value_t::binary:
case detail::value_t::discarded:
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr));
JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
}
}
@ -532,6 +553,7 @@ class 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
*/
template<typename BasicJsonType>
bool contains(const BasicJsonType* ptr) const
{
for (const auto& reference_token : reference_tokens)
@ -579,7 +601,7 @@ class json_pointer
}
}
const auto idx = array_index(reference_token);
const auto idx = array_index<BasicJsonType>(reference_token);
if (idx >= ptr->size())
{
// index out of range
@ -620,9 +642,9 @@ class json_pointer
@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)
static std::vector<string_t> split(const string_t& reference_string)
{
std::vector<std::string> result;
std::vector<string_t> result;
// special case: empty reference string -> no reference tokens
if (reference_string.empty())
@ -633,7 +655,7 @@ class json_pointer
// check if nonempty reference string begins with slash
if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))
{
JSON_THROW(detail::parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'", BasicJsonType()));
JSON_THROW(detail::parse_error::create(107, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr));
}
// extract the reference tokens:
@ -644,11 +666,11 @@ class json_pointer
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 == 0 (if slash == std::string::npos)
// we can stop if start == 0 (if slash == string_t::npos)
start != 0;
// set the beginning of the next reference token
// (will eventually be 0 if slash == std::string::npos)
start = (slash == std::string::npos) ? 0 : slash + 1,
// (will eventually be 0 if slash == string_t::npos)
start = (slash == string_t::npos) ? 0 : slash + 1,
// find next slash
slash = reference_string.find_first_of('/', start))
{
@ -658,7 +680,7 @@ class json_pointer
// check reference tokens are properly escaped
for (std::size_t pos = reference_token.find_first_of('~');
pos != std::string::npos;
pos != string_t::npos;
pos = reference_token.find_first_of('~', pos + 1))
{
JSON_ASSERT(reference_token[pos] == '~');
@ -668,7 +690,7 @@ class json_pointer
(reference_token[pos + 1] != '0' &&
reference_token[pos + 1] != '1')))
{
JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", BasicJsonType()));
JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", nullptr));
}
}
@ -688,7 +710,8 @@ class json_pointer
@note Empty objects or arrays are flattened to `null`.
*/
static void flatten(const std::string& reference_string,
template<typename BasicJsonType>
static void flatten(const string_t& reference_string,
const BasicJsonType& value,
BasicJsonType& result)
{
@ -706,7 +729,7 @@ class json_pointer
// 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),
flatten(detail::concat(reference_string, '/', std::to_string(i)),
value.m_value.array->operator[](i), result);
}
}
@ -725,7 +748,7 @@ class json_pointer
// iterate object and use keys as reference string
for (const auto& element : *value.m_value.object)
{
flatten(reference_string + "/" + detail::escape(element.first), element.second, result);
flatten(detail::concat(reference_string, '/', detail::escape(element.first)), element.second, result);
}
}
break;
@ -758,12 +781,13 @@ class json_pointer
@throw type_error.315 if object values are not primitive
@throw type_error.313 if value cannot be unflattened
*/
template<typename BasicJsonType>
static BasicJsonType
unflatten(const BasicJsonType& value)
{
if (JSON_HEDLEY_UNLIKELY(!value.is_object()))
{
JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", value));
JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", &value));
}
BasicJsonType result;
@ -773,7 +797,7 @@ class json_pointer
{
if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))
{
JSON_THROW(detail::type_error::create(315, "values in object must be primitive", element.second));
JSON_THROW(detail::type_error::create(315, "values in object must be primitive", &element.second));
}
// assign value to reference pointed to by JSON pointer; Note that if
@ -786,6 +810,21 @@ class json_pointer
return result;
}
// 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;
}
/*!
@brief compares two JSON pointers for equality
@ -797,11 +836,10 @@ class json_pointer
@exceptionsafety No-throw guarantee: this function never throws exceptions.
*/
friend bool operator==(json_pointer const& lhs,
json_pointer const& rhs) noexcept
{
return lhs.reference_tokens == rhs.reference_tokens;
}
template<typename RefStringTypeLhs, typename RefStringTypeRhs>
// NOLINTNEXTLINE(readability-redundant-declaration)
friend bool operator==(json_pointer<RefStringTypeLhs> const& lhs,
json_pointer<RefStringTypeRhs> const& rhs) noexcept;
/*!
@brief compares two JSON pointers for inequality
@ -814,13 +852,27 @@ class json_pointer
@exceptionsafety No-throw guarantee: this function never throws exceptions.
*/
friend bool operator!=(json_pointer const& lhs,
json_pointer const& rhs) noexcept
{
return !(lhs == rhs);
}
template<typename RefStringTypeLhs, typename RefStringTypeRhs>
// NOLINTNEXTLINE(readability-redundant-declaration)
friend bool operator!=(json_pointer<RefStringTypeLhs> const& lhs,
json_pointer<RefStringTypeRhs> const& rhs) noexcept;
/// the reference tokens
std::vector<std::string> reference_tokens;
std::vector<string_t> reference_tokens;
};
// functions cannot be defined inside class due to ODR violations
template<typename RefStringTypeLhs, typename RefStringTypeRhs>
inline bool operator==(json_pointer<RefStringTypeLhs> const& lhs,
json_pointer<RefStringTypeRhs> const& rhs) noexcept
{
return lhs.reference_tokens == rhs.reference_tokens;
}
template<typename RefStringTypeLhs, typename RefStringTypeRhs>
inline bool operator!=(json_pointer<RefStringTypeLhs> const& lhs,
json_pointer<RefStringTypeRhs> const& rhs) noexcept
{
return !(lhs == rhs);
}
} // namespace nlohmann

View File

@ -44,6 +44,21 @@ template<typename> struct is_basic_json : std::false_type {};
NLOHMANN_BASIC_JSON_TPL_DECLARATION
struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};
// used by exceptions create() member functions
// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t
// false_type otherwise
template<typename BasicJsonContext>
struct is_basic_json_context :
std::integral_constant < bool,
is_basic_json<typename std::remove_cv<typename std::remove_pointer<BasicJsonContext>::type>::type>::value
|| std::is_same<BasicJsonContext, std::nullptr_t>::value >
{};
template<typename> struct is_json_pointer : std::false_type {};
template<typename RefStringType>
struct is_json_pointer<json_pointer<RefStringType>> : std::true_type {};
//////////////////////
// json_ref helpers //
//////////////////////

View File

@ -12,6 +12,7 @@
#include <nlohmann/detail/input/binary_reader.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/output/output_adapters.hpp>
#include <nlohmann/detail/string_concat.hpp>
namespace nlohmann
{
@ -67,7 +68,7 @@ class binary_writer
case value_t::discarded:
default:
{
JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()), j));
JSON_THROW(type_error::create(317, concat("to serialize to BSON, top-level type must be object, but is ", j.type_name()), &j));
}
}
}
@ -937,7 +938,7 @@ class binary_writer
const auto it = name.find(static_cast<typename string_t::value_type>(0));
if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos))
{
JSON_THROW(out_of_range::create(409, "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")", j));
JSON_THROW(out_of_range::create(409, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j));
static_cast<void>(j);
}
@ -1062,7 +1063,7 @@ class binary_writer
}
else
{
JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64", j));
JSON_THROW(out_of_range::create(407, concat("integer number ", std::to_string(j.m_value.number_unsigned), " cannot be represented by BSON as it does not fit int64"), &j));
}
}

View File

@ -19,6 +19,7 @@
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/output/binary_writer.hpp>
#include <nlohmann/detail/output/output_adapters.hpp>
#include <nlohmann/detail/string_concat.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
@ -500,7 +501,7 @@ class serializer
{
case error_handler_t::strict:
{
JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + hex_bytes(byte | 0), BasicJsonType()));
JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex_bytes(byte | 0)), nullptr));
}
case error_handler_t::ignore:
@ -592,7 +593,7 @@ class serializer
{
case error_handler_t::strict:
{
JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + hex_bytes(static_cast<std::uint8_t>(s.back() | 0)), BasicJsonType()));
JSON_THROW(type_error::create(316, concat("incomplete UTF-8 string; last byte: 0x", hex_bytes(static_cast<std::uint8_t>(s.back() | 0))), nullptr));
}
case error_handler_t::ignore:

View File

@ -0,0 +1,139 @@
#pragma once
#include <cstring> // strlen
#include <string> // string
#include <utility> // forward
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/detected.hpp>
namespace nlohmann
{
namespace detail
{
inline std::size_t concat_length()
{
return 0;
}
template<typename... Args>
inline std::size_t concat_length(const char* cstr, Args&& ... rest);
template<typename StringType, typename... Args>
inline std::size_t concat_length(const StringType& str, Args&& ... rest);
template<typename... Args>
inline std::size_t concat_length(const char /*c*/, Args&& ... rest)
{
return 1 + concat_length(std::forward<Args>(rest)...);
}
template<typename... Args>
inline std::size_t concat_length(const char* cstr, Args&& ... rest)
{
// cppcheck-suppress ignoredReturnValue
return ::strlen(cstr) + concat_length(std::forward<Args>(rest)...);
}
template<typename StringType, typename... Args>
inline std::size_t concat_length(const StringType& str, Args&& ... rest)
{
return str.size() + concat_length(std::forward<Args>(rest)...);
}
template<typename OutStringType>
inline void concat_into(OutStringType& /*out*/)
{}
template<typename StringType, typename Arg>
using string_can_append = decltype(std::declval<StringType&>().append(std::declval < Arg && > ()));
template<typename StringType, typename Arg>
using detect_string_can_append = is_detected<string_can_append, StringType, Arg>;
template<typename StringType, typename Arg>
using string_can_append_op = decltype(std::declval<StringType&>() += std::declval < Arg && > ());
template<typename StringType, typename Arg>
using detect_string_can_append_op = is_detected<string_can_append_op, StringType, Arg>;
template<typename StringType, typename Arg>
using string_can_append_iter = decltype(std::declval<StringType&>().append(std::declval<const Arg&>().begin(), std::declval<const Arg&>().end()));
template<typename StringType, typename Arg>
using detect_string_can_append_iter = is_detected<string_can_append_iter, StringType, Arg>;
template<typename StringType, typename Arg>
using string_can_append_data = decltype(std::declval<StringType&>().append(std::declval<const Arg&>().data(), std::declval<const Arg&>().size()));
template<typename StringType, typename Arg>
using detect_string_can_append_data = is_detected<string_can_append_data, StringType, Arg>;
template < typename OutStringType, typename Arg, typename... Args,
enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
&& detect_string_can_append_op<OutStringType, Arg>::value, int > = 0 >
inline void concat_into(OutStringType& out, Arg && arg, Args && ... rest);
template < typename OutStringType, typename Arg, typename... Args,
enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
&& !detect_string_can_append_op<OutStringType, Arg>::value
&& detect_string_can_append_iter<OutStringType, Arg>::value, int > = 0 >
inline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest);
template < typename OutStringType, typename Arg, typename... Args,
enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
&& !detect_string_can_append_op<OutStringType, Arg>::value
&& !detect_string_can_append_iter<OutStringType, Arg>::value
&& detect_string_can_append_data<OutStringType, Arg>::value, int > = 0 >
inline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest);
template<typename OutStringType, typename Arg, typename... Args,
enable_if_t<detect_string_can_append<OutStringType, Arg>::value, int> = 0>
inline void concat_into(OutStringType& out, Arg && arg, Args && ... rest)
{
out.append(std::forward<Arg>(arg));
concat_into(out, std::forward<Args>(rest)...);
}
template < typename OutStringType, typename Arg, typename... Args,
enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
&& detect_string_can_append_op<OutStringType, Arg>::value, int > >
inline void concat_into(OutStringType& out, Arg&& arg, Args&& ... rest)
{
out += std::forward<Arg>(arg);
concat_into(out, std::forward<Args>(rest)...);
}
template < typename OutStringType, typename Arg, typename... Args,
enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
&& !detect_string_can_append_op<OutStringType, Arg>::value
&& detect_string_can_append_iter<OutStringType, Arg>::value, int > >
inline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest)
{
out.append(arg.begin(), arg.end());
concat_into(out, std::forward<Args>(rest)...);
}
template < typename OutStringType, typename Arg, typename... Args,
enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
&& !detect_string_can_append_op<OutStringType, Arg>::value
&& !detect_string_can_append_iter<OutStringType, Arg>::value
&& detect_string_can_append_data<OutStringType, Arg>::value, int > >
inline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest)
{
out.append(arg.data(), arg.size());
concat_into(out, std::forward<Args>(rest)...);
}
template<typename OutStringType = std::string, typename... Args>
inline OutStringType concat(Args && ... args)
{
OutStringType str;
str.reserve(concat_length(std::forward<Args>(args)...));
concat_into(str, std::forward<Args>(args)...);
return str;
}
} // namespace detail
} // namespace nlohmann

View File

@ -1,6 +1,5 @@
#pragma once
#include <string>
#include <nlohmann/detail/macro_scope.hpp>
namespace nlohmann
@ -21,12 +20,13 @@ enforced with an assertion.**
@since version 2.0.0
*/
inline void replace_substring(std::string& s, const std::string& f,
const std::string& t)
template<typename StringType>
inline void replace_substring(StringType& s, const StringType& f,
const StringType& t)
{
JSON_ASSERT(!f.empty());
for (auto pos = s.find(f); // find first occurrence of f
pos != std::string::npos; // make sure f was found
pos != StringType::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
{}
@ -39,10 +39,11 @@ inline void replace_substring(std::string& s, const std::string& f,
*
* Note the order of escaping "~" to "~0" and "/" to "~1" is important.
*/
inline std::string escape(std::string s)
template<typename StringType>
inline StringType escape(StringType s)
{
replace_substring(s, "~", "~0");
replace_substring(s, "/", "~1");
replace_substring(s, StringType{"~"}, StringType{"~0"});
replace_substring(s, StringType{"/"}, StringType{"~1"});
return s;
}
@ -53,10 +54,11 @@ inline std::string escape(std::string s)
*
* Note the order of escaping "~1" to "/" and "~0" to "~" is important.
*/
static void unescape(std::string& s)
template<typename StringType>
static void unescape(StringType& s)
{
replace_substring(s, "~1", "/");
replace_substring(s, "~0", "~");
replace_substring(s, StringType{"~1"}, StringType{"/"});
replace_substring(s, StringType{"~0"}, StringType{"~"});
}
} // namespace detail

View File

@ -83,6 +83,7 @@ SOFTWARE.
#include <nlohmann/detail/json_pointer.hpp>
#include <nlohmann/detail/json_ref.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/string_concat.hpp>
#include <nlohmann/detail/string_escape.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>
@ -129,7 +130,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
{
private:
template<detail::value_t> friend struct detail::external_constructor;
friend ::nlohmann::json_pointer<basic_json>;
template<typename>
friend class ::nlohmann::json_pointer;
// can be restored when json_pointer backwards compatibility is removed
// friend ::nlohmann::json_pointer<StringType>;
template<typename BasicJsonType, typename InputType>
friend class ::nlohmann::detail::parser;
@ -188,7 +193,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
public:
using value_t = detail::value_t;
/// JSON Pointer, see @ref nlohmann::json_pointer
using json_pointer = ::nlohmann::json_pointer<basic_json>;
using json_pointer = ::nlohmann::json_pointer<StringType>;
template<typename T, typename SFINAE>
using json_serializer = JSONSerializer<T, SFINAE>;
/// how to treat decoding errors
@ -280,9 +285,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
result["name"] = "JSON for Modern C++";
result["url"] = "https://github.com/nlohmann/json";
result["version"]["string"] =
std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + "." +
std::to_string(NLOHMANN_JSON_VERSION_MINOR) + "." +
std::to_string(NLOHMANN_JSON_VERSION_PATCH);
detail::concat(std::to_string(NLOHMANN_JSON_VERSION_MAJOR), '.',
std::to_string(NLOHMANN_JSON_VERSION_MINOR), '.',
std::to_string(NLOHMANN_JSON_VERSION_PATCH));
result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR;
result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR;
result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH;
@ -304,7 +309,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
#elif defined(__clang__)
result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}};
#elif defined(__GNUC__) || defined(__GNUG__)
result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}};
result["compiler"] = {{"family", "gcc"}, {"version", detail::concat(
std::to_string(__GNUC__), '.',
std::to_string(__GNUC_MINOR__), '.',
std::to_string(__GNUC_PATCHLEVEL__))
}
};
#elif defined(__HP_cc) || defined(__HP_aCC)
result["compiler"] = "hp"
#elif defined(__IBMCPP__)
@ -532,7 +542,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
object = nullptr; // silence warning, see #821
if (JSON_HEDLEY_UNLIKELY(t == value_t::null))
{
JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.10.5", basic_json())); // LCOV_EXCL_LINE
JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.10.5", nullptr)); // LCOV_EXCL_LINE
}
break;
}
@ -930,7 +940,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// if object is wanted but impossible, throw an exception
if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object))
{
JSON_THROW(type_error::create(301, "cannot create object from initializer list", basic_json()));
JSON_THROW(type_error::create(301, "cannot create object from initializer list", nullptr));
}
}
@ -1042,7 +1052,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// make sure iterator fits the current value
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
{
JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", basic_json()));
JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", nullptr));
}
// copy type from first iterator
@ -1060,7 +1070,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
|| !last.m_it.primitive_iterator.is_end()))
{
JSON_THROW(invalid_iterator::create(204, "iterators out of range", *first.m_object));
JSON_THROW(invalid_iterator::create(204, "iterators out of range", first.m_object));
}
break;
}
@ -1129,7 +1139,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
case value_t::null:
case value_t::discarded:
default:
JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + std::string(first.m_object->type_name()), *first.m_object));
JSON_THROW(invalid_iterator::create(206, detail::concat("cannot construct with iterators from ", first.m_object->type_name()), first.m_object));
}
set_parents();
@ -1413,7 +1423,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return m_value.boolean;
}
JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()), *this));
JSON_THROW(type_error::create(302, detail::concat("type must be boolean, but is ", type_name()), this));
}
/// get a pointer to the value (object)
@ -1534,7 +1544,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return *ptr;
}
JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()), obj));
JSON_THROW(type_error::create(303, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj));
}
public:
@ -1910,7 +1920,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
{
if (!is_binary())
{
JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this));
JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this));
}
return *get_ptr<binary_t*>();
@ -1922,7 +1932,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
{
if (!is_binary())
{
JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this));
JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this));
}
return *get_ptr<const binary_t*>();
@ -1953,12 +1963,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_CATCH (std::out_of_range&)
{
// create better exception explanation
JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this));
JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
}
}
else
{
JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
}
}
@ -1976,12 +1986,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_CATCH (std::out_of_range&)
{
// create better exception explanation
JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this));
JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
}
}
else
{
JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
}
}
@ -1999,12 +2009,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_CATCH (std::out_of_range&)
{
// create better exception explanation
JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this));
JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this));
}
}
else
{
JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
}
}
@ -2022,12 +2032,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_CATCH (std::out_of_range&)
{
// create better exception explanation
JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this));
JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this));
}
}
else
{
JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
}
}
@ -2074,7 +2084,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return m_value.array->operator[](idx);
}
JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this));
}
/// @brief access specified array element
@ -2087,7 +2097,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return m_value.array->operator[](idx);
}
JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this));
}
/// @brief access specified object element
@ -2108,7 +2118,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return set_parent(m_value.object->operator[](key));
}
JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
}
/// @brief access specified object element
@ -2122,7 +2132,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return m_value.object->find(key)->second;
}
JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
}
/// @brief access specified object element
@ -2145,7 +2155,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return set_parent(m_value.object->operator[](key));
}
JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
}
/// @brief access specified object element
@ -2161,7 +2171,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return m_value.object->find(key)->second;
}
JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
}
/// @brief access specified object element with default value
@ -2185,7 +2195,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return default_value;
}
JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
}
/// @brief access specified object element with default value
@ -2216,7 +2226,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
}
JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
}
template<class ValueType, class BasicJsonType, typename std::enable_if<
detail::is_getable<basic_json_t, ValueType>::value, int>::type = 0>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
ValueType value(const ::nlohmann::json_pointer<BasicJsonType>& ptr, const ValueType& default_value) const
{
return value(ptr.convert(), default_value);
}
/// @brief access specified object element via JSON Pointer with default value
@ -2228,6 +2246,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return value(ptr, string_t(default_value));
}
template<typename BasicJsonType>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
JSON_HEDLEY_NON_NULL(3)
string_t value(const typename ::nlohmann::json_pointer<BasicJsonType>& ptr, const char* default_value) const
{
return value(ptr.convert(), default_value);
}
/// @brief access the first element
/// @sa https://json.nlohmann.me/api/basic_json/front/
reference front()
@ -2271,7 +2297,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// make sure iterator fits the current value
if (JSON_HEDLEY_UNLIKELY(this != pos.m_object))
{
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this));
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
}
IteratorType result = end();
@ -2287,7 +2313,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
{
if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin()))
{
JSON_THROW(invalid_iterator::create(205, "iterator out of range", *this));
JSON_THROW(invalid_iterator::create(205, "iterator out of range", this));
}
if (is_string())
@ -2325,7 +2351,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
case value_t::null:
case value_t::discarded:
default:
JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
}
return result;
@ -2342,7 +2368,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// make sure iterator fits the current value
if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object))
{
JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", *this));
JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", this));
}
IteratorType result = end();
@ -2359,7 +2385,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin()
|| !last.m_it.primitive_iterator.is_end()))
{
JSON_THROW(invalid_iterator::create(204, "iterators out of range", *this));
JSON_THROW(invalid_iterator::create(204, "iterators out of range", this));
}
if (is_string())
@ -2399,7 +2425,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
case value_t::null:
case value_t::discarded:
default:
JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
}
return result;
@ -2415,7 +2441,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return m_value.object->erase(key);
}
JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
}
/// @brief remove element from a JSON array given an index
@ -2427,14 +2453,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
{
if (JSON_HEDLEY_UNLIKELY(idx >= size()))
{
JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this));
JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
}
m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));
}
else
{
JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
}
}
@ -2490,7 +2516,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @brief check the existence of an element in a JSON object
/// @sa https://json.nlohmann.me/api/basic_json/contains/
template < typename KeyT, typename std::enable_if <
!std::is_same<typename std::decay<KeyT>::type, json_pointer>::value, int >::type = 0 >
!detail::is_json_pointer<typename std::decay<KeyT>::type>::value, int >::type = 0 >
bool contains(KeyT && key) const
{
return is_object() && m_value.object->find(std::forward<KeyT>(key)) != m_value.object->end();
@ -2503,6 +2529,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return ptr.contains(this);
}
template<typename BasicJsonType>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
bool contains(const typename ::nlohmann::json_pointer<BasicJsonType> ptr) const
{
return ptr.contains(this);
}
/// @}
@ -2842,7 +2875,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// push_back only works for null objects or arrays
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
{
JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
}
// transform null object into an array
@ -2875,7 +2908,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// push_back only works for null objects or arrays
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
{
JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
}
// transform null object into an array
@ -2907,7 +2940,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// push_back only works for null objects or objects
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
{
JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
}
// transform null object into an object
@ -2963,7 +2996,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// emplace_back only works for null objects or arrays
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
{
JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(311, detail::concat("cannot use emplace_back() with ", type_name()), this));
}
// transform null object into an array
@ -2988,7 +3021,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// emplace only works for null objects or arrays
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
{
JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(311, detail::concat("cannot use emplace() with ", type_name()), this));
}
// transform null object into an object
@ -3042,14 +3075,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this));
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
}
// insert to array and return iterator
return insert_iterator(pos, val);
}
JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
}
/// @brief inserts element into array
@ -3069,14 +3102,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this));
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
}
// insert to array and return iterator
return insert_iterator(pos, cnt, val);
}
JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
}
/// @brief inserts range of elements into array
@ -3086,24 +3119,24 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// insert only works for arrays
if (JSON_HEDLEY_UNLIKELY(!is_array()))
{
JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
}
// check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this));
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
}
// check if range iterators belong to the same JSON object
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
{
JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this));
JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
}
if (JSON_HEDLEY_UNLIKELY(first.m_object == this))
{
JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", *this));
JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", this));
}
// insert to array and return iterator
@ -3117,13 +3150,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// insert only works for arrays
if (JSON_HEDLEY_UNLIKELY(!is_array()))
{
JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
}
// check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this));
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
}
// insert to array and return iterator
@ -3137,19 +3170,19 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// insert only works for objects
if (JSON_HEDLEY_UNLIKELY(!is_object()))
{
JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
}
// check if range iterators belong to the same JSON object
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
{
JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this));
JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
}
// passed iterators must belong to objects
if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
{
JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", *this));
JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", this));
}
m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);
@ -3176,19 +3209,19 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
if (JSON_HEDLEY_UNLIKELY(!is_object()))
{
JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", type_name()), this));
}
// check if range iterators belong to the same JSON object
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
{
JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this));
JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
}
// passed iterators must belong to objects
if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
{
JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(first.m_object->type_name()), *first.m_object));
JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", first.m_object->type_name()), first.m_object));
}
for (auto it = first; it != last; ++it)
@ -3249,7 +3282,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
else
{
JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(310, detail::concat("cannot use swap() with ", type_name()), this));
}
}
@ -3264,7 +3297,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
else
{
JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(310, detail::concat("cannot use swap() with ", type_name()), this));
}
}
@ -3279,7 +3312,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
else
{
JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(310, detail::concat("cannot use swap() with ", type_name()), this));
}
}
@ -3294,7 +3327,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
else
{
JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(310, detail::concat("cannot use swap() with ", type_name()), this));
}
}
@ -3309,7 +3342,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
else
{
JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this));
JSON_THROW(type_error::create(310, detail::concat("cannot use swap() with ", type_name()), this));
}
}
@ -4191,6 +4224,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return ptr.get_unchecked(this);
}
template<typename BasicJsonType>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr)
{
return ptr.get_unchecked(this);
}
/// @brief access specified element via JSON Pointer
/// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
const_reference operator[](const json_pointer& ptr) const
@ -4198,6 +4238,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return ptr.get_unchecked(this);
}
template<typename BasicJsonType>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
const_reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr) const
{
return ptr.get_unchecked(this);
}
/// @brief access specified element via JSON Pointer
/// @sa https://json.nlohmann.me/api/basic_json/at/
reference at(const json_pointer& ptr)
@ -4205,6 +4252,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return ptr.get_checked(this);
}
template<typename BasicJsonType>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr)
{
return ptr.get_checked(this);
}
/// @brief access specified element via JSON Pointer
/// @sa https://json.nlohmann.me/api/basic_json/at/
const_reference at(const json_pointer& ptr) const
@ -4212,6 +4266,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return ptr.get_checked(this);
}
template<typename BasicJsonType>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
const_reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr) const
{
return ptr.get_checked(this);
}
/// @brief return flattened JSON value
/// @sa https://json.nlohmann.me/api/basic_json/flatten/
basic_json flatten() const
@ -4318,11 +4379,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
else
{
const auto idx = json_pointer::array_index(last_path);
const auto idx = json_pointer::template array_index<basic_json_t>(last_path);
if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))
{
// avoid undefined behavior
JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", parent));
JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), &parent));
}
// default case: insert add offset
@ -4363,20 +4424,20 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
else
{
JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found", *this));
JSON_THROW(out_of_range::create(403, detail::concat("key '", last_path, "' not found"), this));
}
}
else if (parent.is_array())
{
// note erase performs range check
parent.erase(json_pointer::array_index(last_path));
parent.erase(json_pointer::template array_index<basic_json_t>(last_path));
}
};
// type check: top level value must be an array
if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array()))
{
JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", json_patch));
JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &json_patch));
}
// iterate and apply the operations
@ -4391,20 +4452,20 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
auto it = val.m_value.object->find(member);
// context-sensitive error message
const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'";
const auto error_msg = (op == "op") ? "operation" : detail::concat("operation '", op, '\'');
// check if desired value is present
if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end()))
{
// NOLINTNEXTLINE(performance-inefficient-string-concatenation)
JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'", val));
JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have member '", member, "'"), &val));
}
// check if result is of type string
if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string()))
{
// NOLINTNEXTLINE(performance-inefficient-string-concatenation)
JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'", val));
JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have string member '", member, "'"), &val));
}
// no error: return value
@ -4414,7 +4475,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// type check: every element of the array must be an object
if (JSON_HEDLEY_UNLIKELY(!val.is_object()))
{
JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", val));
JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &val));
}
// collect mandatory members
@ -4492,7 +4553,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// throw an exception if test fails
if (JSON_HEDLEY_UNLIKELY(!success))
{
JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump(), val));
JSON_THROW(other_error::create(501, detail::concat("unsuccessful: ", val.dump()), &val));
}
break;
@ -4503,7 +4564,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
{
// op must be "add", "remove", "replace", "move", "copy", or
// "test"
JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid", val));
JSON_THROW(parse_error::create(105, 0, detail::concat("operation value '", op, "' is invalid"), &val));
}
}
}
@ -4545,7 +4606,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
while (i < source.size() && i < target.size())
{
// recursive call to compare array values at index i
auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i));
auto temp_diff = diff(source[i], target[i], detail::concat(path, '/', std::to_string(i)));
result.insert(result.end(), temp_diff.begin(), temp_diff.end());
++i;
}
@ -4562,7 +4623,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
result.insert(result.begin() + end_index, object(
{
{"op", "remove"},
{"path", path + "/" + std::to_string(i)}
{"path", detail::concat(path, '/', std::to_string(i))}
}));
++i;
}
@ -4573,7 +4634,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
result.push_back(
{
{"op", "add"},
{"path", path + "/-"},
{"path", detail::concat(path, "/-")},
{"value", target[i]}
});
++i;
@ -4588,7 +4649,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
for (auto it = source.cbegin(); it != source.cend(); ++it)
{
// escape the key name to be used in a JSON patch
const auto path_key = path + "/" + detail::escape(it.key());
const auto path_key = detail::concat(path, '/', detail::escape(it.key()));
if (target.find(it.key()) != target.end())
{
@ -4612,7 +4673,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
if (source.find(it.key()) == source.end())
{
// found a key that is not in this -> add it
const auto path_key = path + "/" + detail::escape(it.key());
const auto path_key = detail::concat(path, '/', detail::escape(it.key()));
result.push_back(
{
{"op", "add"}, {"path", path_key},

File diff suppressed because it is too large Load Diff

View File

@ -49,6 +49,8 @@ class alt_string
public:
using value_type = std::string::value_type;
static constexpr auto npos = static_cast<std::size_t>(-1);
alt_string(const char* str): str_impl(str) {}
alt_string(const char* str, std::size_t count): str_impl(str, count) {}
alt_string(size_t count, char chr): str_impl(count, chr) {}
@ -144,11 +146,38 @@ class alt_string
str_impl.clear();
}
const value_type* data()
const value_type* data() const
{
return str_impl.data();
}
bool empty() const
{
return str_impl.empty();
}
std::size_t find(const alt_string& str, std::size_t pos = 0) const
{
return str_impl.find(str.str_impl, pos);
}
std::size_t find_first_of(char c, std::size_t pos = 0) const
{
return str_impl.find_first_of(c, pos);
}
alt_string substr(std::size_t pos = 0, std::size_t count = npos) const
{
std::string s = str_impl.substr(pos, count);
return {s.data(), s.size()};
}
alt_string& replace(std::size_t pos, std::size_t count, const alt_string& str)
{
str_impl.replace(pos, count, str.str_impl);
return *this;
}
private:
std::string str_impl {};
@ -296,4 +325,20 @@ TEST_CASE("alternative string type")
CHECK_FALSE(const_doc["Who are you?"] == "I'm Bruce Wayne");
}
}
SECTION("JSON pointer")
{
// conversion from json to alt_json fails to compile (see #3425);
// attempted fix(*) produces: [[['b','a','r'],['b','a','z']]] (with each char being an integer)
// (*) disable implicit conversion for json_refs of any basic_json type
// alt_json j = R"(
// {
// "foo": ["bar", "baz"]
// }
// )"_json;
auto j = alt_json::parse(R"({"foo": ["bar", "baz"]})");
CHECK(j.at(alt_json::json_pointer("/foo/0")) == j["foo"][0]);
CHECK(j.at(alt_json::json_pointer("/foo/1")) == j["foo"][1]);
}
}

View File

@ -37,6 +37,84 @@ using nlohmann::json;
namespace
{
struct alt_string_iter
{
alt_string_iter() = default;
alt_string_iter(const char* cstr)
: impl(cstr)
{}
void reserve(std::size_t s)
{
impl.reserve(s);
}
template<typename Iter>
void append(Iter first, Iter last)
{
impl.append(first, last);
}
std::string::const_iterator begin() const
{
return impl.begin();
}
std::string::const_iterator end() const
{
return impl.end();
}
std::size_t size() const
{
return impl.size();
}
alt_string_iter& operator+=(const char c)
{
impl += c;
return *this;
}
std::string impl{};
};
struct alt_string_data
{
alt_string_data() = default;
alt_string_data(const char* cstr)
: impl(cstr)
{}
void reserve(std::size_t s)
{
impl.reserve(s);
}
void append(const char* p, std::size_t s)
{
impl.append(p, s);
}
const char* data() const
{
return impl.data();
}
std::size_t size() const
{
return impl.size();
}
alt_string_data& operator+=(const char c)
{
impl += c;
return *this;
}
std::string impl{};
};
void check_escaped(const char* original, const char* escaped = "", bool ensure_ascii = false);
void check_escaped(const char* original, const char* escaped, const bool ensure_ascii)
{
@ -110,4 +188,39 @@ TEST_CASE("convenience functions")
CHECK_THROWS_WITH_AS(check_escaped("\xC2"), "[json.exception.type_error.316] incomplete UTF-8 string; last byte: 0xC2", json::type_error&);
}
SECTION("string concat")
{
using nlohmann::detail::concat;
const char* expected = "Hello, world!";
alt_string_iter hello_iter{"Hello, "};
alt_string_data hello_data{"Hello, "};
std::string world = "world";
SECTION("std::string")
{
std::string str1 = concat(hello_iter, world, '!');
std::string str2 = concat(hello_data, world, '!');
std::string str3 = concat("Hello, ", world, '!');
CHECK(str1 == expected);
CHECK(str2 == expected);
CHECK(str3 == expected);
}
SECTION("alt_string_iter")
{
alt_string_iter str = concat<alt_string_iter>(hello_iter, world, '!');
CHECK(str.impl == expected);
}
SECTION("alt_string_data")
{
alt_string_data str = concat<alt_string_data>(hello_data, world, '!');
CHECK(str.impl == expected);
}
}
}

View File

@ -660,4 +660,54 @@ TEST_CASE("JSON pointers")
CHECK(j[ptr] == j["object"]["/"]);
CHECK(ptr.to_string() == "/object/~1");
}
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);
}
}

View File

@ -760,7 +760,6 @@ TEST_CASE("regression tests 2")
{
std::string p = "/root";
// matching types
json test1;
test1[json::json_pointer(p)] = json::object();
CHECK(test1.dump() == "{\"root\":{}}");
@ -769,10 +768,11 @@ TEST_CASE("regression tests 2")
test2[ordered_json::json_pointer(p)] = json::object();
CHECK(test2.dump() == "{\"root\":{}}");
// mixed type - the JSON Pointer is implicitly converted into a string "/root"
// json::json_pointer and ordered_json::json_pointer are the same type; behave as above
ordered_json test3;
test3[json::json_pointer(p)] = json::object();
CHECK(test3.dump() == "{\"/root\":{}}");
CHECK(std::is_same<json::json_pointer::string_t, ordered_json::json_pointer::string_t>::value);
CHECK(test3.dump() == "{\"root\":{}}");
}
SECTION("issue #2982 - to_{binary format} does not provide a mechanism for specifying a custom allocator for the returned type")