mirror of
https://github.com/nlohmann/json.git
synced 2026-09-28 02:30:32 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2666c334e | ||
|
|
ad39cda092 | ||
|
|
8aabb9981f |
@@ -363,7 +363,7 @@ std::cout << j_string << " == " << serialized_string << std::endl;
|
|||||||
|
|
||||||
[`.dump()`](https://json.nlohmann.me/api/basic_json/dump/) returns the originally stored string value.
|
[`.dump()`](https://json.nlohmann.me/api/basic_json/dump/) returns the originally stored string value.
|
||||||
|
|
||||||
Note the library only supports UTF-8. When you store strings with different encodings in the library, calling [`dump()`](https://json.nlohmann.me/api/basic_json/dump/) may throw an exception unless `json::error_handler_t::replace` or `json::error_handler_t::ignore` are used as error handlers.
|
Note the library only supports UTF-8. When you store strings with different encodings in the library, calling [`dump()`](https://json.nlohmann.me/api/basic_json/dump/) may throw an exception unless `json::error_handler_t::replace`, `json::error_handler_t::ignore`, or `json::error_handler_t::keep` are used as error handlers.
|
||||||
|
|
||||||
#### To/from streams (e.g., files, string streams)
|
#### To/from streams (e.g., files, string streams)
|
||||||
|
|
||||||
@@ -1915,7 +1915,7 @@ The library supports **Unicode input** as follows:
|
|||||||
- [Unicode noncharacters](https://www.unicode.org/faq/private_use.html#nonchar1) will not be replaced by the library.
|
- [Unicode noncharacters](https://www.unicode.org/faq/private_use.html#nonchar1) will not be replaced by the library.
|
||||||
- Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors.
|
- Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors.
|
||||||
- The strings stored in the library are UTF-8 encoded. When using the default string type (`std::string`), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs.
|
- The strings stored in the library are UTF-8 encoded. When using the default string type (`std::string`), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs.
|
||||||
- When you store strings with different encodings in the library, calling [`dump()`](https://json.nlohmann.me/api/basic_json/dump/) may throw an exception unless `json::error_handler_t::replace` or `json::error_handler_t::ignore` are used as error handlers.
|
- When you store strings with different encodings in the library, calling [`dump()`](https://json.nlohmann.me/api/basic_json/dump/) may throw an exception unless `json::error_handler_t::replace`, `json::error_handler_t::ignore`, or `json::error_handler_t::keep` are used as error handlers.
|
||||||
- To store wide strings (e.g., `std::wstring`), you need to convert them to a UTF-8 encoded `std::string` before, see [an example](https://json.nlohmann.me/home/faq/#wide-string-handling).
|
- To store wide strings (e.g., `std::wstring`), you need to convert them to a UTF-8 encoded `std::string` before, see [an example](https://json.nlohmann.me/home/faq/#wide-string-handling).
|
||||||
|
|
||||||
### Comments in JSON
|
### Comments in JSON
|
||||||
|
|||||||
@@ -25,10 +25,15 @@ and `ensure_ascii` parameters.
|
|||||||
result consists of ASCII characters only.
|
result consists of ASCII characters only.
|
||||||
|
|
||||||
`error_handler` (in)
|
`error_handler` (in)
|
||||||
: how to react on decoding errors; there are three possible values (see [`error_handler_t`](error_handler_t.md):
|
: how to react on decoding errors; there are four possible values (see [`error_handler_t`](error_handler_t.md)):
|
||||||
`strict` (throws an exception in case a decoding error occurs; default), `replace` (replace invalid UTF-8 sequences
|
|
||||||
with U+FFFD), and `ignore` (ignore invalid UTF-8 sequences during serialization; all valid bytes are copied to the
|
- `strict`: throw a [`type_error`](../../home/exceptions.md#type-errors) exception in case a decoding error occurs
|
||||||
output unchanged, and invalid bytes are dropped)).
|
(default),
|
||||||
|
- `replace`: replace invalid UTF-8 sequences with U+FFFD (� REPLACEMENT CHARACTER),
|
||||||
|
- `ignore`: ignore invalid UTF-8 sequences during serialization; all valid bytes are copied to the output unchanged,
|
||||||
|
and invalid bytes are dropped, and
|
||||||
|
- `keep`: keep invalid UTF-8 sequences during serialization; all bytes are copied to the output unchanged, so the
|
||||||
|
result is not valid UTF-8.
|
||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
@@ -94,3 +99,4 @@ Binary values are serialized as an object containing two keys:
|
|||||||
- Indentation character `indent_char`, option `ensure_ascii` and exceptions added in version 3.0.0.
|
- Indentation character `indent_char`, option `ensure_ascii` and exceptions added in version 3.0.0.
|
||||||
- Error handlers added in version 3.4.0.
|
- Error handlers added in version 3.4.0.
|
||||||
- Serialization of binary values added in version 3.8.0.
|
- Serialization of binary values added in version 3.8.0.
|
||||||
|
- Error handler value `keep` added in version 3.13.0.
|
||||||
|
|||||||
@@ -4,12 +4,13 @@
|
|||||||
enum class error_handler_t {
|
enum class error_handler_t {
|
||||||
strict,
|
strict,
|
||||||
replace,
|
replace,
|
||||||
ignore
|
ignore,
|
||||||
|
keep
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
This enumeration is used in the [`dump`](dump.md) function to choose how to treat decoding errors while serializing a
|
This enumeration is used in the [`dump`](dump.md) function to choose how to treat decoding errors while serializing a
|
||||||
`basic_json` value. Three values are differentiated:
|
`basic_json` value. Four values are differentiated:
|
||||||
|
|
||||||
strict
|
strict
|
||||||
: throw a `type_error` exception in case of invalid UTF-8
|
: throw a `type_error` exception in case of invalid UTF-8
|
||||||
@@ -20,6 +21,12 @@ replace
|
|||||||
ignore
|
ignore
|
||||||
: ignore invalid UTF-8 sequences; all valid bytes are copied to the output unchanged, and invalid bytes are dropped
|
: ignore invalid UTF-8 sequences; all valid bytes are copied to the output unchanged, and invalid bytes are dropped
|
||||||
|
|
||||||
|
keep
|
||||||
|
: keep invalid UTF-8 sequences; all bytes are copied to the output unchanged. Valid characters are still escaped as
|
||||||
|
usual (e.g., `"`, `\\`, and control characters), so the result has valid JSON syntax, but it is not valid UTF-8.
|
||||||
|
In particular, [`parse`](parse.md) rejects it, and with `ensure_ascii` set to `true`, the invalid bytes are the
|
||||||
|
only non-ASCII bytes of the output.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
@@ -40,3 +47,4 @@ ignore
|
|||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 3.4.0.
|
- Added in version 3.4.0.
|
||||||
|
- Added value `keep` in version 3.13.0.
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#include <iomanip>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
@@ -21,4 +22,12 @@ int main()
|
|||||||
<< "\nstring with ignored invalid characters: "
|
<< "\nstring with ignored invalid characters: "
|
||||||
<< j_invalid.dump(-1, ' ', false, json::error_handler_t::ignore)
|
<< j_invalid.dump(-1, ' ', false, json::error_handler_t::ignore)
|
||||||
<< '\n';
|
<< '\n';
|
||||||
|
|
||||||
|
// the invalid byte is kept; print the result byte-wise to make it visible
|
||||||
|
std::cout << "string with kept invalid characters:";
|
||||||
|
for (const unsigned char c : j_invalid.dump(-1, ' ', false, json::error_handler_t::keep))
|
||||||
|
{
|
||||||
|
std::cout << ' ' << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(c);
|
||||||
|
}
|
||||||
|
std::cout << '\n';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9
|
[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9
|
||||||
string with replaced invalid characters: "ä�ü"
|
string with replaced invalid characters: "ä�ü"
|
||||||
string with ignored invalid characters: "äü"
|
string with ignored invalid characters: "äü"
|
||||||
|
string with kept invalid characters: 22 c3 a4 a9 c3 bc 22
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ serialization fails by default. The fourth argument of `dump` selects an
|
|||||||
- `strict` (default) — throw a [`type_error.316`](../home/exceptions.md#jsonexceptiontype_error316) exception.
|
- `strict` (default) — throw a [`type_error.316`](../home/exceptions.md#jsonexceptiontype_error316) exception.
|
||||||
- `replace` — replace invalid bytes with the Unicode replacement character U+FFFD (`�`).
|
- `replace` — replace invalid bytes with the Unicode replacement character U+FFFD (`�`).
|
||||||
- `ignore` — silently drop invalid bytes.
|
- `ignore` — silently drop invalid bytes.
|
||||||
|
- `keep` — copy invalid bytes to the output unchanged; the result is not valid UTF-8.
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
|
|
||||||
|
|||||||
@@ -755,6 +755,7 @@ The `dump()` function only works with UTF-8 encoded strings; that is, if you ass
|
|||||||
- Pass an error handler as last parameter to the `dump()` function to avoid this exception:
|
- Pass an error handler as last parameter to the `dump()` function to avoid this exception:
|
||||||
- `json::error_handler_t::replace` will replace invalid bytes sequences with `U+FFFD`
|
- `json::error_handler_t::replace` will replace invalid bytes sequences with `U+FFFD`
|
||||||
- `json::error_handler_t::ignore` will silently ignore invalid byte sequences
|
- `json::error_handler_t::ignore` will silently ignore invalid byte sequences
|
||||||
|
- `json::error_handler_t::keep` will copy invalid byte sequences to the output unchanged
|
||||||
|
|
||||||
### json.exception.type_error.317
|
### json.exception.type_error.317
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ The library supports **Unicode input** as follows:
|
|||||||
- The library will not replace [Unicode noncharacters](http://www.unicode.org/faq/private_use.html#nonchar1).
|
- The library will not replace [Unicode noncharacters](http://www.unicode.org/faq/private_use.html#nonchar1).
|
||||||
- Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors.
|
- Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors.
|
||||||
- The strings stored in the library are UTF-8 encoded. When using the default string type (`std::string`), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs.
|
- The strings stored in the library are UTF-8 encoded. When using the default string type (`std::string`), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs.
|
||||||
- When you store strings with different encodings in the library, calling [`dump()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a50ec80b02d0f3f51130d4abb5d1cfdc5.html#a50ec80b02d0f3f51130d4abb5d1cfdc5) may throw an exception unless `json::error_handler_t::replace` or `json::error_handler_t::ignore` are used as error handlers.
|
- When you store strings with different encodings in the library, calling [`dump()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a50ec80b02d0f3f51130d4abb5d1cfdc5.html#a50ec80b02d0f3f51130d4abb5d1cfdc5) may throw an exception unless `json::error_handler_t::replace`, `json::error_handler_t::ignore`, or `json::error_handler_t::keep` are used as error handlers.
|
||||||
|
|
||||||
In most cases, the parser is right to complain, because the input is not UTF-8 encoded. This is especially true for Microsoft Windows, where Latin-1 or ISO 8859-1 is often the standard encoding.
|
In most cases, the parser is right to complain, because the input is not UTF-8 encoded. This is especially true for Microsoft Windows, where Latin-1 or ISO 8859-1 is often the standard encoding.
|
||||||
|
|
||||||
|
|||||||
@@ -42,10 +42,6 @@ namespace detail
|
|||||||
* j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an
|
* j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an
|
||||||
* allocated value (e.g., a string). See bug issue
|
* allocated value (e.g., a string). See bug issue
|
||||||
* https://github.com/nlohmann/json/issues/2865 for more information.
|
* https://github.com/nlohmann/json/issues/2865 for more information.
|
||||||
*
|
|
||||||
* A value that has to be allocated is created before the old one is destroyed:
|
|
||||||
* were it the other way around, an exception while creating the new value would
|
|
||||||
* leave j with the type of the new value, but the pointer to the destroyed old one.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
template<value_t> struct external_constructor;
|
template<value_t> struct external_constructor;
|
||||||
@@ -69,20 +65,18 @@ struct external_constructor<value_t::string>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
||||||
{
|
{
|
||||||
const typename BasicJsonType::json_value value(s);
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::string;
|
j.m_data.m_type = value_t::string;
|
||||||
j.m_data.m_value = value;
|
j.m_data.m_value = s;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
||||||
{
|
{
|
||||||
const typename BasicJsonType::json_value value(std::move(s));
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::string;
|
j.m_data.m_type = value_t::string;
|
||||||
j.m_data.m_value = value;
|
j.m_data.m_value = std::move(s);
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,10 +85,9 @@ struct external_constructor<value_t::string>
|
|||||||
int > = 0 >
|
int > = 0 >
|
||||||
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
||||||
{
|
{
|
||||||
auto* created = j.template create<typename BasicJsonType::string_t>(str);
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::string;
|
j.m_data.m_type = value_t::string;
|
||||||
j.m_data.m_value.string = created;
|
j.m_data.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -105,20 +98,18 @@ struct external_constructor<value_t::binary>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
||||||
{
|
{
|
||||||
const typename BasicJsonType::json_value value(b);
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::binary;
|
j.m_data.m_type = value_t::binary;
|
||||||
j.m_data.m_value = value;
|
j.m_data.m_value = typename BasicJsonType::binary_t(b);
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
||||||
{
|
{
|
||||||
const typename BasicJsonType::json_value value(std::move(b));
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::binary;
|
j.m_data.m_type = value_t::binary;
|
||||||
j.m_data.m_value = value;
|
j.m_data.m_value = typename BasicJsonType::binary_t(std::move(b));
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -168,10 +159,9 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
||||||
{
|
{
|
||||||
const typename BasicJsonType::json_value value(arr);
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value = value;
|
j.m_data.m_value = arr;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -179,10 +169,9 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
||||||
{
|
{
|
||||||
const typename BasicJsonType::json_value value(std::move(arr));
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value = value;
|
j.m_data.m_value = std::move(arr);
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -198,10 +187,9 @@ struct external_constructor<value_t::array>
|
|||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
auto* created = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value.array = created;
|
j.m_data.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -209,17 +197,15 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
||||||
{
|
{
|
||||||
typename BasicJsonType::array_t elements;
|
|
||||||
elements.reserve(arr.size());
|
|
||||||
for (const bool x : arr)
|
|
||||||
{
|
|
||||||
elements.push_back(x);
|
|
||||||
}
|
|
||||||
const typename BasicJsonType::json_value value(std::move(elements));
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value = value;
|
j.m_data.m_value = value_t::array;
|
||||||
j.set_parents();
|
j.m_data.m_value.array->reserve(arr.size());
|
||||||
|
for (const bool x : arr)
|
||||||
|
{
|
||||||
|
j.m_data.m_value.array->push_back(x);
|
||||||
|
j.set_parent(j.m_data.m_value.array->back());
|
||||||
|
}
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,12 +213,11 @@ struct external_constructor<value_t::array>
|
|||||||
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
||||||
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
||||||
{
|
{
|
||||||
typename BasicJsonType::array_t elements(arr.size());
|
|
||||||
std::copy(std::begin(arr), std::end(arr), elements.begin());
|
|
||||||
const typename BasicJsonType::json_value value(std::move(elements));
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value = value;
|
j.m_data.m_value = value_t::array;
|
||||||
|
j.m_data.m_value.array->resize(arr.size());
|
||||||
|
std::copy(std::begin(arr), std::end(arr), j.m_data.m_value.array->begin());
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -244,16 +229,14 @@ struct external_constructor<value_t::array>
|
|||||||
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
|
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
|
||||||
static void construct(BasicJsonType& j, CompatibleArrayType && arr)
|
static void construct(BasicJsonType& j, CompatibleArrayType && arr)
|
||||||
{
|
{
|
||||||
typename BasicJsonType::array_t elements;
|
|
||||||
for (auto&& x : std::forward<CompatibleArrayType>(arr))
|
|
||||||
{
|
|
||||||
elements.push_back(x);
|
|
||||||
}
|
|
||||||
const typename BasicJsonType::json_value value(std::move(elements));
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value = value;
|
j.m_data.m_value = value_t::array;
|
||||||
j.set_parents();
|
for (auto&& x : std::forward<CompatibleArrayType>(arr))
|
||||||
|
{
|
||||||
|
j.m_data.m_value.array->push_back(x);
|
||||||
|
j.set_parent(j.m_data.m_value.array->back());
|
||||||
|
}
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -265,10 +248,9 @@ struct external_constructor<value_t::object>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
||||||
{
|
{
|
||||||
const typename BasicJsonType::json_value value(obj);
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::object;
|
j.m_data.m_type = value_t::object;
|
||||||
j.m_data.m_value = value;
|
j.m_data.m_value = obj;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -276,10 +258,9 @@ struct external_constructor<value_t::object>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
||||||
{
|
{
|
||||||
const typename BasicJsonType::json_value value(std::move(obj));
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::object;
|
j.m_data.m_type = value_t::object;
|
||||||
j.m_data.m_value = value;
|
j.m_data.m_value = std::move(obj);
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -291,10 +272,9 @@ struct external_constructor<value_t::object>
|
|||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
auto* created = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::object;
|
j.m_data.m_type = value_t::object;
|
||||||
j.m_data.m_value.object = created;
|
j.m_data.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,8 @@ enum class error_handler_t
|
|||||||
{
|
{
|
||||||
strict, ///< throw a type_error exception in case of invalid UTF-8
|
strict, ///< throw a type_error exception in case of invalid UTF-8
|
||||||
replace, ///< replace invalid UTF-8 sequences with U+FFFD
|
replace, ///< replace invalid UTF-8 sequences with U+FFFD
|
||||||
ignore ///< ignore invalid UTF-8 sequences
|
ignore, ///< ignore invalid UTF-8 sequences
|
||||||
|
keep ///< keep invalid UTF-8 sequences; their bytes are copied unchanged
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
@@ -1019,6 +1020,47 @@ class serializer
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case error_handler_t::keep:
|
||||||
|
{
|
||||||
|
// drop whatever the incomplete sequence left in
|
||||||
|
// the buffer (only copied if !EnsureAscii) and copy
|
||||||
|
// the ill-formed bytes from the input instead
|
||||||
|
bytes = bytes_after_last_accept;
|
||||||
|
|
||||||
|
if (undumped_chars > 0)
|
||||||
|
{
|
||||||
|
// the pending bytes of the incomplete sequence
|
||||||
|
// are ill-formed; the current byte may be OK for
|
||||||
|
// itself, so we would like to read it again
|
||||||
|
for (std::size_t j = i - undumped_chars; j < i; ++j)
|
||||||
|
{
|
||||||
|
string_buffer[bytes++] = s[j];
|
||||||
|
}
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// the current byte cannot start any sequence
|
||||||
|
string_buffer[bytes++] = s[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// write buffer and reset index; there must be 13 bytes
|
||||||
|
// left, as this is the maximal number of bytes to be
|
||||||
|
// written ("\uxxxx\uxxxx\0") for one code point
|
||||||
|
if (string_buffer.size() - bytes < 13)
|
||||||
|
{
|
||||||
|
put_buffer(string_buffer, bytes);
|
||||||
|
bytes = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_after_last_accept = bytes;
|
||||||
|
undumped_chars = 0;
|
||||||
|
|
||||||
|
// continue processing the string
|
||||||
|
state = UTF8_ACCEPT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default: // LCOV_EXCL_LINE
|
default: // LCOV_EXCL_LINE
|
||||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
@@ -1064,6 +1106,15 @@ class serializer
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case error_handler_t::keep:
|
||||||
|
{
|
||||||
|
// write all accepted bytes
|
||||||
|
put_buffer(string_buffer, bytes_after_last_accept);
|
||||||
|
// copy the bytes of the incomplete sequence unchanged
|
||||||
|
put_string(s, s.size() - undumped_chars, s.size());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case error_handler_t::replace:
|
case error_handler_t::replace:
|
||||||
{
|
{
|
||||||
// write all accepted bytes
|
// write all accepted bytes
|
||||||
|
|||||||
+15
-15
@@ -1702,8 +1702,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
if (is_an_object)
|
if (is_an_object)
|
||||||
{
|
{
|
||||||
// the initializer list is a list of pairs -> create an object
|
// the initializer list is a list of pairs -> create an object
|
||||||
m_data.m_value = value_t::object;
|
|
||||||
m_data.m_type = value_t::object;
|
m_data.m_type = value_t::object;
|
||||||
|
m_data.m_value = value_t::object;
|
||||||
|
|
||||||
for (auto& element_ref : init)
|
for (auto& element_ref : init)
|
||||||
{
|
{
|
||||||
@@ -1725,8 +1725,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// the initializer list describes an array -> create an array
|
// the initializer list describes an array -> create an array
|
||||||
m_data.m_value.array = create<array_t>(init.begin(), init.end());
|
|
||||||
m_data.m_type = value_t::array;
|
m_data.m_type = value_t::array;
|
||||||
|
m_data.m_value.array = create<array_t>(init.begin(), init.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
set_parents();
|
set_parents();
|
||||||
@@ -1739,8 +1739,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(const typename binary_t::container_type& init)
|
static basic_json binary(const typename binary_t::container_type& init)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_value = init;
|
|
||||||
res.m_data.m_type = value_t::binary;
|
res.m_data.m_type = value_t::binary;
|
||||||
|
res.m_data.m_value = init;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1750,8 +1750,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)
|
static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_value = binary_t(init, subtype);
|
|
||||||
res.m_data.m_type = value_t::binary;
|
res.m_data.m_type = value_t::binary;
|
||||||
|
res.m_data.m_value = binary_t(init, subtype);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1761,8 +1761,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(typename binary_t::container_type&& init)
|
static basic_json binary(typename binary_t::container_type&& init)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_value = std::move(init);
|
|
||||||
res.m_data.m_type = value_t::binary;
|
res.m_data.m_type = value_t::binary;
|
||||||
|
res.m_data.m_value = std::move(init);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1772,8 +1772,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)
|
static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_value = binary_t(std::move(init), subtype);
|
|
||||||
res.m_data.m_type = value_t::binary;
|
res.m_data.m_type = value_t::binary;
|
||||||
|
res.m_data.m_value = binary_t(std::move(init), subtype);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2819,8 +2819,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// implicitly convert a null value to an empty array
|
// implicitly convert a null value to an empty array
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_value.array = create<array_t>();
|
|
||||||
m_data.m_type = value_t::array;
|
m_data.m_type = value_t::array;
|
||||||
|
m_data.m_value.array = create<array_t>();
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2879,8 +2879,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// implicitly convert a null value to an empty object
|
// implicitly convert a null value to an empty object
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_value.object = create<object_t>();
|
|
||||||
m_data.m_type = value_t::object;
|
m_data.m_type = value_t::object;
|
||||||
|
m_data.m_value.object = create<object_t>();
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2932,8 +2932,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// implicitly convert a null value to an empty object
|
// implicitly convert a null value to an empty object
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_value.object = create<object_t>();
|
|
||||||
m_data.m_type = value_t::object;
|
m_data.m_type = value_t::object;
|
||||||
|
m_data.m_value.object = create<object_t>();
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3868,8 +3868,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// transform a null object into an array
|
// transform a null object into an array
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_value = value_t::array;
|
|
||||||
m_data.m_type = value_t::array;
|
m_data.m_type = value_t::array;
|
||||||
|
m_data.m_value = value_t::array;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3901,8 +3901,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// transform a null object into an array
|
// transform a null object into an array
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_value = value_t::array;
|
|
||||||
m_data.m_type = value_t::array;
|
m_data.m_type = value_t::array;
|
||||||
|
m_data.m_value = value_t::array;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3933,8 +3933,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// transform a null object into an object
|
// transform a null object into an object
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_value = value_t::object;
|
|
||||||
m_data.m_type = value_t::object;
|
m_data.m_type = value_t::object;
|
||||||
|
m_data.m_value = value_t::object;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3989,8 +3989,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// transform a null object into an array
|
// transform a null object into an array
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_value = value_t::array;
|
|
||||||
m_data.m_type = value_t::array;
|
m_data.m_type = value_t::array;
|
||||||
|
m_data.m_value = value_t::array;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4014,8 +4014,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// transform a null object into an object
|
// transform a null object into an object
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_value = value_t::object;
|
|
||||||
m_data.m_type = value_t::object;
|
m_data.m_type = value_t::object;
|
||||||
|
m_data.m_value = value_t::object;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4196,8 +4196,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// implicitly convert a null value to an empty object
|
// implicitly convert a null value to an empty object
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_value.object = create<object_t>();
|
|
||||||
m_data.m_type = value_t::object;
|
m_data.m_type = value_t::object;
|
||||||
|
m_data.m_value.object = create<object_t>();
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6571,10 +6571,6 @@ namespace detail
|
|||||||
* j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an
|
* j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an
|
||||||
* allocated value (e.g., a string). See bug issue
|
* allocated value (e.g., a string). See bug issue
|
||||||
* https://github.com/nlohmann/json/issues/2865 for more information.
|
* https://github.com/nlohmann/json/issues/2865 for more information.
|
||||||
*
|
|
||||||
* A value that has to be allocated is created before the old one is destroyed:
|
|
||||||
* were it the other way around, an exception while creating the new value would
|
|
||||||
* leave j with the type of the new value, but the pointer to the destroyed old one.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
template<value_t> struct external_constructor;
|
template<value_t> struct external_constructor;
|
||||||
@@ -6598,20 +6594,18 @@ struct external_constructor<value_t::string>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
||||||
{
|
{
|
||||||
const typename BasicJsonType::json_value value(s);
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::string;
|
j.m_data.m_type = value_t::string;
|
||||||
j.m_data.m_value = value;
|
j.m_data.m_value = s;
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
||||||
{
|
{
|
||||||
const typename BasicJsonType::json_value value(std::move(s));
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::string;
|
j.m_data.m_type = value_t::string;
|
||||||
j.m_data.m_value = value;
|
j.m_data.m_value = std::move(s);
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6620,10 +6614,9 @@ struct external_constructor<value_t::string>
|
|||||||
int > = 0 >
|
int > = 0 >
|
||||||
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
||||||
{
|
{
|
||||||
auto* created = j.template create<typename BasicJsonType::string_t>(str);
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::string;
|
j.m_data.m_type = value_t::string;
|
||||||
j.m_data.m_value.string = created;
|
j.m_data.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -6634,20 +6627,18 @@ struct external_constructor<value_t::binary>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
||||||
{
|
{
|
||||||
const typename BasicJsonType::json_value value(b);
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::binary;
|
j.m_data.m_type = value_t::binary;
|
||||||
j.m_data.m_value = value;
|
j.m_data.m_value = typename BasicJsonType::binary_t(b);
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
||||||
{
|
{
|
||||||
const typename BasicJsonType::json_value value(std::move(b));
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::binary;
|
j.m_data.m_type = value_t::binary;
|
||||||
j.m_data.m_value = value;
|
j.m_data.m_value = typename BasicJsonType::binary_t(std::move(b));
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -6697,10 +6688,9 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
||||||
{
|
{
|
||||||
const typename BasicJsonType::json_value value(arr);
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value = value;
|
j.m_data.m_value = arr;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -6708,10 +6698,9 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
||||||
{
|
{
|
||||||
const typename BasicJsonType::json_value value(std::move(arr));
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value = value;
|
j.m_data.m_value = std::move(arr);
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -6727,10 +6716,9 @@ struct external_constructor<value_t::array>
|
|||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
auto* created = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value.array = created;
|
j.m_data.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -6738,17 +6726,15 @@ struct external_constructor<value_t::array>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
||||||
{
|
{
|
||||||
typename BasicJsonType::array_t elements;
|
|
||||||
elements.reserve(arr.size());
|
|
||||||
for (const bool x : arr)
|
|
||||||
{
|
|
||||||
elements.push_back(x);
|
|
||||||
}
|
|
||||||
const typename BasicJsonType::json_value value(std::move(elements));
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value = value;
|
j.m_data.m_value = value_t::array;
|
||||||
j.set_parents();
|
j.m_data.m_value.array->reserve(arr.size());
|
||||||
|
for (const bool x : arr)
|
||||||
|
{
|
||||||
|
j.m_data.m_value.array->push_back(x);
|
||||||
|
j.set_parent(j.m_data.m_value.array->back());
|
||||||
|
}
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6756,12 +6742,11 @@ struct external_constructor<value_t::array>
|
|||||||
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
||||||
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
||||||
{
|
{
|
||||||
typename BasicJsonType::array_t elements(arr.size());
|
|
||||||
std::copy(std::begin(arr), std::end(arr), elements.begin());
|
|
||||||
const typename BasicJsonType::json_value value(std::move(elements));
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value = value;
|
j.m_data.m_value = value_t::array;
|
||||||
|
j.m_data.m_value.array->resize(arr.size());
|
||||||
|
std::copy(std::begin(arr), std::end(arr), j.m_data.m_value.array->begin());
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -6773,16 +6758,14 @@ struct external_constructor<value_t::array>
|
|||||||
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
|
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
|
||||||
static void construct(BasicJsonType& j, CompatibleArrayType && arr)
|
static void construct(BasicJsonType& j, CompatibleArrayType && arr)
|
||||||
{
|
{
|
||||||
typename BasicJsonType::array_t elements;
|
|
||||||
for (auto&& x : std::forward<CompatibleArrayType>(arr))
|
|
||||||
{
|
|
||||||
elements.push_back(x);
|
|
||||||
}
|
|
||||||
const typename BasicJsonType::json_value value(std::move(elements));
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::array;
|
j.m_data.m_type = value_t::array;
|
||||||
j.m_data.m_value = value;
|
j.m_data.m_value = value_t::array;
|
||||||
j.set_parents();
|
for (auto&& x : std::forward<CompatibleArrayType>(arr))
|
||||||
|
{
|
||||||
|
j.m_data.m_value.array->push_back(x);
|
||||||
|
j.set_parent(j.m_data.m_value.array->back());
|
||||||
|
}
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -6794,10 +6777,9 @@ struct external_constructor<value_t::object>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
||||||
{
|
{
|
||||||
const typename BasicJsonType::json_value value(obj);
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::object;
|
j.m_data.m_type = value_t::object;
|
||||||
j.m_data.m_value = value;
|
j.m_data.m_value = obj;
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -6805,10 +6787,9 @@ struct external_constructor<value_t::object>
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
||||||
{
|
{
|
||||||
const typename BasicJsonType::json_value value(std::move(obj));
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::object;
|
j.m_data.m_type = value_t::object;
|
||||||
j.m_data.m_value = value;
|
j.m_data.m_value = std::move(obj);
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -6820,10 +6801,9 @@ struct external_constructor<value_t::object>
|
|||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
auto* created = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
|
||||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||||
j.m_data.m_type = value_t::object;
|
j.m_data.m_type = value_t::object;
|
||||||
j.m_data.m_value.object = created;
|
j.m_data.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
||||||
j.set_parents();
|
j.set_parents();
|
||||||
j.assert_invariant();
|
j.assert_invariant();
|
||||||
}
|
}
|
||||||
@@ -23951,7 +23931,8 @@ enum class error_handler_t
|
|||||||
{
|
{
|
||||||
strict, ///< throw a type_error exception in case of invalid UTF-8
|
strict, ///< throw a type_error exception in case of invalid UTF-8
|
||||||
replace, ///< replace invalid UTF-8 sequences with U+FFFD
|
replace, ///< replace invalid UTF-8 sequences with U+FFFD
|
||||||
ignore ///< ignore invalid UTF-8 sequences
|
ignore, ///< ignore invalid UTF-8 sequences
|
||||||
|
keep ///< keep invalid UTF-8 sequences; their bytes are copied unchanged
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
@@ -24922,6 +24903,47 @@ class serializer
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case error_handler_t::keep:
|
||||||
|
{
|
||||||
|
// drop whatever the incomplete sequence left in
|
||||||
|
// the buffer (only copied if !EnsureAscii) and copy
|
||||||
|
// the ill-formed bytes from the input instead
|
||||||
|
bytes = bytes_after_last_accept;
|
||||||
|
|
||||||
|
if (undumped_chars > 0)
|
||||||
|
{
|
||||||
|
// the pending bytes of the incomplete sequence
|
||||||
|
// are ill-formed; the current byte may be OK for
|
||||||
|
// itself, so we would like to read it again
|
||||||
|
for (std::size_t j = i - undumped_chars; j < i; ++j)
|
||||||
|
{
|
||||||
|
string_buffer[bytes++] = s[j];
|
||||||
|
}
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// the current byte cannot start any sequence
|
||||||
|
string_buffer[bytes++] = s[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// write buffer and reset index; there must be 13 bytes
|
||||||
|
// left, as this is the maximal number of bytes to be
|
||||||
|
// written ("\uxxxx\uxxxx\0") for one code point
|
||||||
|
if (string_buffer.size() - bytes < 13)
|
||||||
|
{
|
||||||
|
put_buffer(string_buffer, bytes);
|
||||||
|
bytes = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_after_last_accept = bytes;
|
||||||
|
undumped_chars = 0;
|
||||||
|
|
||||||
|
// continue processing the string
|
||||||
|
state = UTF8_ACCEPT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default: // LCOV_EXCL_LINE
|
default: // LCOV_EXCL_LINE
|
||||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
@@ -24967,6 +24989,15 @@ class serializer
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case error_handler_t::keep:
|
||||||
|
{
|
||||||
|
// write all accepted bytes
|
||||||
|
put_buffer(string_buffer, bytes_after_last_accept);
|
||||||
|
// copy the bytes of the incomplete sequence unchanged
|
||||||
|
put_string(s, s.size() - undumped_chars, s.size());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case error_handler_t::replace:
|
case error_handler_t::replace:
|
||||||
{
|
{
|
||||||
// write all accepted bytes
|
// write all accepted bytes
|
||||||
@@ -27605,8 +27636,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
if (is_an_object)
|
if (is_an_object)
|
||||||
{
|
{
|
||||||
// the initializer list is a list of pairs -> create an object
|
// the initializer list is a list of pairs -> create an object
|
||||||
m_data.m_value = value_t::object;
|
|
||||||
m_data.m_type = value_t::object;
|
m_data.m_type = value_t::object;
|
||||||
|
m_data.m_value = value_t::object;
|
||||||
|
|
||||||
for (auto& element_ref : init)
|
for (auto& element_ref : init)
|
||||||
{
|
{
|
||||||
@@ -27628,8 +27659,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// the initializer list describes an array -> create an array
|
// the initializer list describes an array -> create an array
|
||||||
m_data.m_value.array = create<array_t>(init.begin(), init.end());
|
|
||||||
m_data.m_type = value_t::array;
|
m_data.m_type = value_t::array;
|
||||||
|
m_data.m_value.array = create<array_t>(init.begin(), init.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
set_parents();
|
set_parents();
|
||||||
@@ -27642,8 +27673,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(const typename binary_t::container_type& init)
|
static basic_json binary(const typename binary_t::container_type& init)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_value = init;
|
|
||||||
res.m_data.m_type = value_t::binary;
|
res.m_data.m_type = value_t::binary;
|
||||||
|
res.m_data.m_value = init;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27653,8 +27684,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)
|
static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_value = binary_t(init, subtype);
|
|
||||||
res.m_data.m_type = value_t::binary;
|
res.m_data.m_type = value_t::binary;
|
||||||
|
res.m_data.m_value = binary_t(init, subtype);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27664,8 +27695,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(typename binary_t::container_type&& init)
|
static basic_json binary(typename binary_t::container_type&& init)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_value = std::move(init);
|
|
||||||
res.m_data.m_type = value_t::binary;
|
res.m_data.m_type = value_t::binary;
|
||||||
|
res.m_data.m_value = std::move(init);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27675,8 +27706,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)
|
static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)
|
||||||
{
|
{
|
||||||
auto res = basic_json();
|
auto res = basic_json();
|
||||||
res.m_data.m_value = binary_t(std::move(init), subtype);
|
|
||||||
res.m_data.m_type = value_t::binary;
|
res.m_data.m_type = value_t::binary;
|
||||||
|
res.m_data.m_value = binary_t(std::move(init), subtype);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28722,8 +28753,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// implicitly convert a null value to an empty array
|
// implicitly convert a null value to an empty array
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_value.array = create<array_t>();
|
|
||||||
m_data.m_type = value_t::array;
|
m_data.m_type = value_t::array;
|
||||||
|
m_data.m_value.array = create<array_t>();
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28782,8 +28813,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// implicitly convert a null value to an empty object
|
// implicitly convert a null value to an empty object
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_value.object = create<object_t>();
|
|
||||||
m_data.m_type = value_t::object;
|
m_data.m_type = value_t::object;
|
||||||
|
m_data.m_value.object = create<object_t>();
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28835,8 +28866,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// implicitly convert a null value to an empty object
|
// implicitly convert a null value to an empty object
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_value.object = create<object_t>();
|
|
||||||
m_data.m_type = value_t::object;
|
m_data.m_type = value_t::object;
|
||||||
|
m_data.m_value.object = create<object_t>();
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29771,8 +29802,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// transform a null object into an array
|
// transform a null object into an array
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_value = value_t::array;
|
|
||||||
m_data.m_type = value_t::array;
|
m_data.m_type = value_t::array;
|
||||||
|
m_data.m_value = value_t::array;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29804,8 +29835,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// transform a null object into an array
|
// transform a null object into an array
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_value = value_t::array;
|
|
||||||
m_data.m_type = value_t::array;
|
m_data.m_type = value_t::array;
|
||||||
|
m_data.m_value = value_t::array;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29836,8 +29867,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// transform a null object into an object
|
// transform a null object into an object
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_value = value_t::object;
|
|
||||||
m_data.m_type = value_t::object;
|
m_data.m_type = value_t::object;
|
||||||
|
m_data.m_value = value_t::object;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29892,8 +29923,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// transform a null object into an array
|
// transform a null object into an array
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_value = value_t::array;
|
|
||||||
m_data.m_type = value_t::array;
|
m_data.m_type = value_t::array;
|
||||||
|
m_data.m_value = value_t::array;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29917,8 +29948,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// transform a null object into an object
|
// transform a null object into an object
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_value = value_t::object;
|
|
||||||
m_data.m_type = value_t::object;
|
m_data.m_type = value_t::object;
|
||||||
|
m_data.m_value = value_t::object;
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30099,8 +30130,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// implicitly convert a null value to an empty object
|
// implicitly convert a null value to an empty object
|
||||||
if (is_null())
|
if (is_null())
|
||||||
{
|
{
|
||||||
m_data.m_value.object = create<object_t>();
|
|
||||||
m_data.m_type = value_t::object;
|
m_data.m_type = value_t::object;
|
||||||
|
m_data.m_value.object = create<object_t>();
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,11 +12,6 @@
|
|||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
#include <valarray>
|
|
||||||
#if JSON_HAS_RANGES
|
|
||||||
#include <ranges>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
// special test case to check if memory is leaked if constructor throws
|
// special test case to check if memory is leaked if constructor throws
|
||||||
@@ -399,173 +394,3 @@ TEST_CASE("bad my_allocator::construct")
|
|||||||
j["test"].push_back("should not leak");
|
j["test"].push_back("should not leak");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// the no-exceptions CI job skips every CHECK_THROWS_AS, which would leave
|
|
||||||
// next_construct_fails set for the next allocation outside a check
|
|
||||||
#if !defined(JSON_NOEXCEPTION)
|
|
||||||
TEST_CASE("a failed allocation leaves the value unchanged")
|
|
||||||
{
|
|
||||||
// create JSON type using the throwing allocator
|
|
||||||
using my_json = nlohmann::basic_json<std::map,
|
|
||||||
std::vector,
|
|
||||||
std::string,
|
|
||||||
bool,
|
|
||||||
std::int64_t,
|
|
||||||
std::uint64_t,
|
|
||||||
double,
|
|
||||||
my_allocator>;
|
|
||||||
|
|
||||||
// Each of these creates a string, array, object, or binary value. The
|
|
||||||
// value must be created before the type is changed: otherwise, a failed
|
|
||||||
// creation left a value of the new type without anything behind it (an
|
|
||||||
// assertion in its destructor, a null pointer everywhere else) or, when
|
|
||||||
// an old value was destroyed first, with a pointer to that destroyed one.
|
|
||||||
|
|
||||||
SECTION("creating a binary value")
|
|
||||||
{
|
|
||||||
const std::vector<std::uint8_t> bytes = {1, 2, 3};
|
|
||||||
my_json _;
|
|
||||||
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(_ = my_json::binary(bytes), std::bad_alloc&);
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(_ = my_json::binary(bytes, 42), std::bad_alloc&);
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(_ = my_json::binary(std::vector<std::uint8_t>(bytes)), std::bad_alloc&);
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(_ = my_json::binary(std::vector<std::uint8_t>(bytes), 42), std::bad_alloc&);
|
|
||||||
next_construct_fails = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("turning a null value into an array or object")
|
|
||||||
{
|
|
||||||
my_json j;
|
|
||||||
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(j[0], std::bad_alloc&);
|
|
||||||
CHECK(j.is_null());
|
|
||||||
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(j["key"], std::bad_alloc&);
|
|
||||||
CHECK(j.is_null());
|
|
||||||
|
|
||||||
#ifdef JSON_HAS_CPP_17
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(j[std::string_view("key")], std::bad_alloc&);
|
|
||||||
CHECK(j.is_null());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(j.push_back(my_json(1)), std::bad_alloc&);
|
|
||||||
CHECK(j.is_null());
|
|
||||||
|
|
||||||
const my_json one = 1;
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(j.push_back(one), std::bad_alloc&);
|
|
||||||
CHECK(j.is_null());
|
|
||||||
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(j.push_back(my_json::object_t::value_type("key", 1)), std::bad_alloc&);
|
|
||||||
CHECK(j.is_null());
|
|
||||||
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(j.emplace_back(1), std::bad_alloc&);
|
|
||||||
CHECK(j.is_null());
|
|
||||||
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(j.emplace("key", 1), std::bad_alloc&);
|
|
||||||
CHECK(j.is_null());
|
|
||||||
|
|
||||||
const my_json object = {{"key", 1}};
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(j.update(object), std::bad_alloc&);
|
|
||||||
CHECK(j.is_null());
|
|
||||||
|
|
||||||
next_construct_fails = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("converting into an existing value")
|
|
||||||
{
|
|
||||||
// to_json replaces the value it is given; the old one must survive a
|
|
||||||
// failed creation of the new one
|
|
||||||
my_json j = "old";
|
|
||||||
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(nlohmann::to_json(j, std::string("new")), std::bad_alloc&);
|
|
||||||
CHECK(j == "old");
|
|
||||||
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(nlohmann::to_json(j, std::vector<int> {1, 2}), std::bad_alloc&);
|
|
||||||
CHECK(j == "old");
|
|
||||||
|
|
||||||
// with iterator debugging, the default constructor of VS 2015's
|
|
||||||
// std::vector is noexcept but constructs a proxy with the allocator,
|
|
||||||
// which terminates when this allocator throws
|
|
||||||
#if !(defined(_MSC_VER) && _MSC_VER < 1910 && defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL > 0)
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(nlohmann::to_json(j, std::vector<bool> {true, false}), std::bad_alloc&);
|
|
||||||
CHECK(j == "old");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(nlohmann::to_json(j, std::map<std::string, int> {{"a", 1}}), std::bad_alloc&);
|
|
||||||
CHECK(j == "old");
|
|
||||||
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(nlohmann::to_json(j, my_json::binary_t({1, 2})), std::bad_alloc&);
|
|
||||||
CHECK(j == "old");
|
|
||||||
|
|
||||||
// the overloads for lvalues of the value types, for the value types
|
|
||||||
// themselves, and for the remaining compatible types
|
|
||||||
const std::string string = "new";
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(nlohmann::to_json(j, string), std::bad_alloc&);
|
|
||||||
CHECK(j == "old");
|
|
||||||
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(nlohmann::to_json(j, "new"), std::bad_alloc&);
|
|
||||||
CHECK(j == "old");
|
|
||||||
|
|
||||||
// to_json only moves a binary value that it converted from another
|
|
||||||
// container type, which my_json's std::vector<std::uint8_t> is not
|
|
||||||
using binary_constructor = nlohmann::detail::external_constructor<nlohmann::detail::value_t::binary>;
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(binary_constructor::construct(j, my_json::binary_t({1, 2})), std::bad_alloc&);
|
|
||||||
CHECK(j == "old");
|
|
||||||
|
|
||||||
my_json::array_t array = {1, 2};
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(nlohmann::to_json(j, array), std::bad_alloc&);
|
|
||||||
CHECK(j == "old");
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(nlohmann::to_json(j, std::move(array)), std::bad_alloc&);
|
|
||||||
CHECK(j == "old");
|
|
||||||
|
|
||||||
my_json::object_t object = {{"a", 1}};
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(nlohmann::to_json(j, object), std::bad_alloc&);
|
|
||||||
CHECK(j == "old");
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(nlohmann::to_json(j, std::move(object)), std::bad_alloc&);
|
|
||||||
CHECK(j == "old");
|
|
||||||
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(nlohmann::to_json(j, std::valarray<int> {1, 2}), std::bad_alloc&);
|
|
||||||
CHECK(j == "old");
|
|
||||||
|
|
||||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
|
||||||
const std::vector<int> numbers = {1, 2};
|
|
||||||
next_construct_fails = true;
|
|
||||||
CHECK_THROWS_AS(nlohmann::to_json(j, numbers | std::views::filter([](int /*unused*/)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
})), std::bad_alloc&);
|
|
||||||
CHECK(j == "old");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
next_construct_fails = false;
|
|
||||||
nlohmann::to_json(j, std::vector<int> {1, 2});
|
|
||||||
CHECK(j == my_json({1, 2}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -766,6 +766,15 @@ TEST_CASE("regression tests 2")
|
|||||||
CHECK(j == k);
|
CHECK(j == k);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("issue #4552 - UTF-8 invalid characters are not always ignored when dumping with error_handler_t::ignore")
|
||||||
|
{
|
||||||
|
json node;
|
||||||
|
node["test"] = "test\334\005";
|
||||||
|
CHECK(node.dump(-1, ' ', false, json::error_handler_t::ignore) == "{\"test\":\"test\\u0005\"}");
|
||||||
|
CHECK(node.dump(-1, ' ', false, json::error_handler_t::keep) == "{\"test\":\"test\334\\u0005\"}");
|
||||||
|
CHECK(node.dump(-1, ' ', true, json::error_handler_t::keep) == "{\"test\":\"test\334\\u0005\"}");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("regression test - parser callback must not lose a duplicate key's prior value")
|
TEST_CASE("regression test - parser callback must not lose a duplicate key's prior value")
|
||||||
|
|||||||
@@ -92,6 +92,8 @@ TEST_CASE("serialization")
|
|||||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"äü\"");
|
CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"äü\"");
|
||||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"ä\xEF\xBF\xBDü\"");
|
CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"ä\xEF\xBF\xBDü\"");
|
||||||
CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"\\u00e4\\ufffd\\u00fc\"");
|
CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"\\u00e4\\ufffd\\u00fc\"");
|
||||||
|
CHECK(j.dump(-1, ' ', false, json::error_handler_t::keep) == "\"ä\xA9ü\"");
|
||||||
|
CHECK(j.dump(-1, ' ', true, json::error_handler_t::keep) == "\"\\u00e4\xA9\\u00fc\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("invalid character (regression guard for shared UTF-8 decoder, see #5529)")
|
SECTION("invalid character (regression guard for shared UTF-8 decoder, see #5529)")
|
||||||
@@ -114,6 +116,8 @@ TEST_CASE("serialization")
|
|||||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"123\"");
|
CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"123\"");
|
||||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"123\xEF\xBF\xBD\"");
|
CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"123\xEF\xBF\xBD\"");
|
||||||
CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"123\\ufffd\"");
|
CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"123\\ufffd\"");
|
||||||
|
CHECK(j.dump(-1, ' ', false, json::error_handler_t::keep) == "\"123\xC2\"");
|
||||||
|
CHECK(j.dump(-1, ' ', true, json::error_handler_t::keep) == "\"123\xC2\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("unexpected character")
|
SECTION("unexpected character")
|
||||||
@@ -126,6 +130,39 @@ TEST_CASE("serialization")
|
|||||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"123456\"");
|
CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"123456\"");
|
||||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"123\xEF\xBF\xBD\x34\x35\x36\"");
|
CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"123\xEF\xBF\xBD\x34\x35\x36\"");
|
||||||
CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"123\\ufffd456\"");
|
CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"123\\ufffd456\"");
|
||||||
|
CHECK(j.dump(-1, ' ', false, json::error_handler_t::keep) == "\"123\xF1\xB0\x34\x35\x36\"");
|
||||||
|
CHECK(j.dump(-1, ' ', true, json::error_handler_t::keep) == "\"123\xF1\xB0\x34\x35\x36\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("keep: valid characters are still escaped")
|
||||||
|
{
|
||||||
|
// an invalid byte followed by characters that must be escaped
|
||||||
|
const json j = "\xC2\"\\\n\xFF\x05";
|
||||||
|
CHECK(j.dump(-1, ' ', false, json::error_handler_t::keep) == "\"\xC2\\\"\\\\\\n\xFF\\u0005\"");
|
||||||
|
CHECK(j.dump(-1, ' ', true, json::error_handler_t::keep) == "\"\xC2\\\"\\\\\\n\xFF\\u0005\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("keep: truncated multibyte sequences")
|
||||||
|
{
|
||||||
|
CHECK(json("\xF0\x9F\x98").dump(-1, ' ', false, json::error_handler_t::keep) == "\"\xF0\x9F\x98\"");
|
||||||
|
CHECK(json("\xF0\x9F\x98").dump(-1, ' ', true, json::error_handler_t::keep) == "\"\xF0\x9F\x98\"");
|
||||||
|
CHECK(json("\xF0\x9F\x98" "a").dump(-1, ' ', false, json::error_handler_t::keep) == "\"\xF0\x9F\x98" "a\"");
|
||||||
|
CHECK(json("\xF0\x9F\x98" "a").dump(-1, ' ', true, json::error_handler_t::keep) == "\"\xF0\x9F\x98" "a\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("keep: long string with many invalid bytes")
|
||||||
|
{
|
||||||
|
// exceeds the internal string buffer several times
|
||||||
|
std::string input;
|
||||||
|
std::string expected = "\"";
|
||||||
|
for (int i = 0; i < 2000; ++i)
|
||||||
|
{
|
||||||
|
input += "\xFF\xE2\x82\n\xC3\xA4";
|
||||||
|
expected += "\xFF\xE2\x82\\n\xC3\xA4";
|
||||||
|
}
|
||||||
|
expected += "\"";
|
||||||
|
const json j = input;
|
||||||
|
CHECK(j.dump(-1, ' ', false, json::error_handler_t::keep) == expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("U+FFFD Substitution of Maximal Subparts")
|
SECTION("U+FFFD Substitution of Maximal Subparts")
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -75,8 +76,11 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
static std::string s_replaced2;
|
static std::string s_replaced2;
|
||||||
static std::string s_replaced_ascii;
|
static std::string s_replaced_ascii;
|
||||||
static std::string s_replaced2_ascii;
|
static std::string s_replaced2_ascii;
|
||||||
|
static std::string s_kept;
|
||||||
|
static std::string s_kept2;
|
||||||
|
static std::string s_kept_ascii;
|
||||||
|
|
||||||
// dumping with ignore/replace must not throw in any case
|
// dumping with ignore/replace/keep must not throw in any case
|
||||||
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||||
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||||
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||||
@@ -85,6 +89,9 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||||
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||||
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||||
|
s_kept = j.dump(-1, ' ', false, json::error_handler_t::keep);
|
||||||
|
s_kept2 = j2.dump(-1, ' ', false, json::error_handler_t::keep);
|
||||||
|
s_kept_ascii = j.dump(-1, ' ', true, json::error_handler_t::keep);
|
||||||
|
|
||||||
if (success_expected)
|
if (success_expected)
|
||||||
{
|
{
|
||||||
@@ -94,6 +101,7 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
// all dumps should agree on the string
|
// all dumps should agree on the string
|
||||||
CHECK(s_strict == s_ignored);
|
CHECK(s_strict == s_ignored);
|
||||||
CHECK(s_strict == s_replaced);
|
CHECK(s_strict == s_replaced);
|
||||||
|
CHECK(s_strict == s_kept);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -105,6 +113,20 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
|
|
||||||
// check that replace string contains a replacement character
|
// check that replace string contains a replacement character
|
||||||
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
||||||
|
|
||||||
|
// ignore drops the invalid bytes, keep copies them
|
||||||
|
CHECK(s_ignored != s_kept);
|
||||||
|
CHECK(s_ignored_ascii != s_kept_ascii);
|
||||||
|
|
||||||
|
// unless a byte needs escaping, keep copies the input unchanged
|
||||||
|
const bool needs_escaping = std::any_of(json_string.begin(), json_string.end(), [](char c)
|
||||||
|
{
|
||||||
|
return static_cast<unsigned char>(c) < 0x20 || c == '"' || c == '\\';
|
||||||
|
});
|
||||||
|
if (!needs_escaping)
|
||||||
|
{
|
||||||
|
CHECK(s_kept == "\"" + json_string + "\"");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check that prefix and suffix are preserved
|
// check that prefix and suffix are preserved
|
||||||
@@ -116,6 +138,8 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
||||||
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
||||||
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
||||||
|
CHECK(s_kept2.substr(1, 3) == "abc");
|
||||||
|
CHECK(s_kept2.substr(s_kept2.size() - 4, 3) == "xyz");
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -75,8 +76,11 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
static std::string s_replaced2;
|
static std::string s_replaced2;
|
||||||
static std::string s_replaced_ascii;
|
static std::string s_replaced_ascii;
|
||||||
static std::string s_replaced2_ascii;
|
static std::string s_replaced2_ascii;
|
||||||
|
static std::string s_kept;
|
||||||
|
static std::string s_kept2;
|
||||||
|
static std::string s_kept_ascii;
|
||||||
|
|
||||||
// dumping with ignore/replace must not throw in any case
|
// dumping with ignore/replace/keep must not throw in any case
|
||||||
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||||
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||||
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||||
@@ -85,6 +89,9 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||||
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||||
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||||
|
s_kept = j.dump(-1, ' ', false, json::error_handler_t::keep);
|
||||||
|
s_kept2 = j2.dump(-1, ' ', false, json::error_handler_t::keep);
|
||||||
|
s_kept_ascii = j.dump(-1, ' ', true, json::error_handler_t::keep);
|
||||||
|
|
||||||
if (success_expected)
|
if (success_expected)
|
||||||
{
|
{
|
||||||
@@ -94,6 +101,7 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
// all dumps should agree on the string
|
// all dumps should agree on the string
|
||||||
CHECK(s_strict == s_ignored);
|
CHECK(s_strict == s_ignored);
|
||||||
CHECK(s_strict == s_replaced);
|
CHECK(s_strict == s_replaced);
|
||||||
|
CHECK(s_strict == s_kept);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -105,6 +113,20 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
|
|
||||||
// check that replace string contains a replacement character
|
// check that replace string contains a replacement character
|
||||||
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
||||||
|
|
||||||
|
// ignore drops the invalid bytes, keep copies them
|
||||||
|
CHECK(s_ignored != s_kept);
|
||||||
|
CHECK(s_ignored_ascii != s_kept_ascii);
|
||||||
|
|
||||||
|
// unless a byte needs escaping, keep copies the input unchanged
|
||||||
|
const bool needs_escaping = std::any_of(json_string.begin(), json_string.end(), [](char c)
|
||||||
|
{
|
||||||
|
return static_cast<unsigned char>(c) < 0x20 || c == '"' || c == '\\';
|
||||||
|
});
|
||||||
|
if (!needs_escaping)
|
||||||
|
{
|
||||||
|
CHECK(s_kept == "\"" + json_string + "\"");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check that prefix and suffix are preserved
|
// check that prefix and suffix are preserved
|
||||||
@@ -116,6 +138,8 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
||||||
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
||||||
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
||||||
|
CHECK(s_kept2.substr(1, 3) == "abc");
|
||||||
|
CHECK(s_kept2.substr(s_kept2.size() - 4, 3) == "xyz");
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -75,8 +76,11 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
static std::string s_replaced2;
|
static std::string s_replaced2;
|
||||||
static std::string s_replaced_ascii;
|
static std::string s_replaced_ascii;
|
||||||
static std::string s_replaced2_ascii;
|
static std::string s_replaced2_ascii;
|
||||||
|
static std::string s_kept;
|
||||||
|
static std::string s_kept2;
|
||||||
|
static std::string s_kept_ascii;
|
||||||
|
|
||||||
// dumping with ignore/replace must not throw in any case
|
// dumping with ignore/replace/keep must not throw in any case
|
||||||
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||||
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||||
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||||
@@ -85,6 +89,9 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||||
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||||
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||||
|
s_kept = j.dump(-1, ' ', false, json::error_handler_t::keep);
|
||||||
|
s_kept2 = j2.dump(-1, ' ', false, json::error_handler_t::keep);
|
||||||
|
s_kept_ascii = j.dump(-1, ' ', true, json::error_handler_t::keep);
|
||||||
|
|
||||||
if (success_expected)
|
if (success_expected)
|
||||||
{
|
{
|
||||||
@@ -94,6 +101,7 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
// all dumps should agree on the string
|
// all dumps should agree on the string
|
||||||
CHECK(s_strict == s_ignored);
|
CHECK(s_strict == s_ignored);
|
||||||
CHECK(s_strict == s_replaced);
|
CHECK(s_strict == s_replaced);
|
||||||
|
CHECK(s_strict == s_kept);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -105,6 +113,20 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
|
|
||||||
// check that replace string contains a replacement character
|
// check that replace string contains a replacement character
|
||||||
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
||||||
|
|
||||||
|
// ignore drops the invalid bytes, keep copies them
|
||||||
|
CHECK(s_ignored != s_kept);
|
||||||
|
CHECK(s_ignored_ascii != s_kept_ascii);
|
||||||
|
|
||||||
|
// unless a byte needs escaping, keep copies the input unchanged
|
||||||
|
const bool needs_escaping = std::any_of(json_string.begin(), json_string.end(), [](char c)
|
||||||
|
{
|
||||||
|
return static_cast<unsigned char>(c) < 0x20 || c == '"' || c == '\\';
|
||||||
|
});
|
||||||
|
if (!needs_escaping)
|
||||||
|
{
|
||||||
|
CHECK(s_kept == "\"" + json_string + "\"");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check that prefix and suffix are preserved
|
// check that prefix and suffix are preserved
|
||||||
@@ -116,6 +138,8 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
||||||
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
||||||
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
||||||
|
CHECK(s_kept2.substr(1, 3) == "abc");
|
||||||
|
CHECK(s_kept2.substr(s_kept2.size() - 4, 3) == "xyz");
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -75,8 +76,11 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
static std::string s_replaced2;
|
static std::string s_replaced2;
|
||||||
static std::string s_replaced_ascii;
|
static std::string s_replaced_ascii;
|
||||||
static std::string s_replaced2_ascii;
|
static std::string s_replaced2_ascii;
|
||||||
|
static std::string s_kept;
|
||||||
|
static std::string s_kept2;
|
||||||
|
static std::string s_kept_ascii;
|
||||||
|
|
||||||
// dumping with ignore/replace must not throw in any case
|
// dumping with ignore/replace/keep must not throw in any case
|
||||||
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||||
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||||
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||||
@@ -85,6 +89,9 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||||
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||||
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||||
|
s_kept = j.dump(-1, ' ', false, json::error_handler_t::keep);
|
||||||
|
s_kept2 = j2.dump(-1, ' ', false, json::error_handler_t::keep);
|
||||||
|
s_kept_ascii = j.dump(-1, ' ', true, json::error_handler_t::keep);
|
||||||
|
|
||||||
if (success_expected)
|
if (success_expected)
|
||||||
{
|
{
|
||||||
@@ -94,6 +101,7 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
// all dumps should agree on the string
|
// all dumps should agree on the string
|
||||||
CHECK(s_strict == s_ignored);
|
CHECK(s_strict == s_ignored);
|
||||||
CHECK(s_strict == s_replaced);
|
CHECK(s_strict == s_replaced);
|
||||||
|
CHECK(s_strict == s_kept);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -105,6 +113,20 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
|
|
||||||
// check that replace string contains a replacement character
|
// check that replace string contains a replacement character
|
||||||
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
||||||
|
|
||||||
|
// ignore drops the invalid bytes, keep copies them
|
||||||
|
CHECK(s_ignored != s_kept);
|
||||||
|
CHECK(s_ignored_ascii != s_kept_ascii);
|
||||||
|
|
||||||
|
// unless a byte needs escaping, keep copies the input unchanged
|
||||||
|
const bool needs_escaping = std::any_of(json_string.begin(), json_string.end(), [](char c)
|
||||||
|
{
|
||||||
|
return static_cast<unsigned char>(c) < 0x20 || c == '"' || c == '\\';
|
||||||
|
});
|
||||||
|
if (!needs_escaping)
|
||||||
|
{
|
||||||
|
CHECK(s_kept == "\"" + json_string + "\"");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check that prefix and suffix are preserved
|
// check that prefix and suffix are preserved
|
||||||
@@ -116,6 +138,8 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
||||||
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
||||||
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
||||||
|
CHECK(s_kept2.substr(1, 3) == "abc");
|
||||||
|
CHECK(s_kept2.substr(s_kept2.size() - 4, 3) == "xyz");
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||||
|
|||||||
Reference in New Issue
Block a user