1
0
mirror of https://github.com/nlohmann/json.git synced 2025-07-31 10:24:23 +03:00

Add ignore_trailing_commas option (#4609)

Added examples and modified the corresponding documents and unit tests.

Signed-off-by: chirsz-ever <chirsz-ever@outlook.com>
Co-authored-by: Niels Lohmann <niels.lohmann@gmail.com>
This commit is contained in:
chirsz
2025-05-22 14:01:46 +08:00
committed by GitHub
parent 2d9a251266
commit 4b17f90f65
15 changed files with 386 additions and 148 deletions

View File

@ -1801,7 +1801,17 @@ This library does not support comments by default. It does so for three reasons:
3. It is dangerous for interoperability if some libraries would add comment support while others don't. Please check [The Harmful Consequences of the Robustness Principle](https://tools.ietf.org/html/draft-iab-protocol-maintenance-01) on this.
However, you can pass set parameter `ignore_comments` to true in the `parse` function to ignore `//` or `/* */` comments. Comments will then be treated as whitespace.
However, you can set set parameter `ignore_comments` to true in the `parse` function to ignore `//` or `/* */` comments. Comments will then be treated as whitespace.
### Trailing commas
The JSON specification does not allow trailing commas in arrays and objects, and hence this library is treating them as parsing errors by default.
Like comments, you can set parameter `ignore_trailing_commas` to true in the `parse` function to ignore trailing commas in arrays and objects. Note that a single comma as the only content of the array or object (`[,]` or `{,}`) is not allowed, and multiple trailing commas (`[1,,]`) are not allowed either.
This library does not add trailing commas when serializing JSON data.
For more information, see [JSON With Commas and Comments (JWCC)](https://nigeltao.github.io/blog/2021/json-with-commas-comments.html).
### Order of object keys

View File

@ -4,12 +4,14 @@
// (1)
template<typename InputType>
static bool accept(InputType&& i,
const bool ignore_comments = false);
const bool ignore_comments = false,
const bool ignore_trailing_commas = false);
// (2)
template<typename IteratorType>
static bool accept(IteratorType first, IteratorType last,
const bool ignore_comments = false);
const bool ignore_comments = false,
const bool ignore_trailing_commas = false);
```
Checks whether the input is valid JSON.
@ -50,6 +52,10 @@ Unlike the [`parse()`](parse.md) function, this function neither throws an excep
: whether comments should be ignored and treated like whitespace (`#!cpp true`) or yield a parse error
(`#!cpp false`); (optional, `#!cpp false` by default)
`ignore_trailing_commas` (in)
: whether trailing commas in arrays or objects should be ignored and treated like whitespace (`#!cpp true`) or yield a parse error
(`#!cpp false`); (optional, `#!cpp false` by default)
`first` (in)
: iterator to the start of the character range
@ -102,6 +108,7 @@ A UTF-8 byte order mark is silently ignored.
- Added in version 3.0.0.
- Ignoring comments via `ignore_comments` added in version 3.9.0.
- Changed [runtime assertion](../../features/assertions.md) in case of `FILE*` null pointers to exception in version 3.12.0.
- Added `ignore_trailing_commas` in version 3.12.1.
!!! warning "Deprecation"

View File

@ -6,14 +6,16 @@ template<typename InputType>
static basic_json parse(InputType&& i,
const parser_callback_t cb = nullptr,
const bool allow_exceptions = true,
const bool ignore_comments = false);
const bool ignore_comments = false,
const bool ignore_trailing_commas = false);
// (2)
template<typename IteratorType>
static basic_json parse(IteratorType first, IteratorType last,
const parser_callback_t cb = nullptr,
const bool allow_exceptions = true,
const bool ignore_comments = false);
const bool ignore_comments = false,
const bool ignore_trailing_commas = false);
```
1. Deserialize from a compatible input.
@ -56,6 +58,10 @@ static basic_json parse(IteratorType first, IteratorType last,
: whether comments should be ignored and treated like whitespace (`#!cpp true`) or yield a parse error
(`#!cpp false`); (optional, `#!cpp false` by default)
`ignore_trailing_commas` (in)
: whether trailing commas in arrays or objects should be ignored and treated like whitespace (`#!cpp true`) or yield a parse error
(`#!cpp false`); (optional, `#!cpp false` by default)
`first` (in)
: iterator to the start of a character range
@ -189,6 +195,34 @@ A UTF-8 byte order mark is silently ignored.
--8<-- "examples/parse__allow_exceptions.output"
```
??? example "Effect of `ignore_comments` parameter"
The example below demonstrates the effect of the `ignore_comments` parameter in the `parse()` function.
```cpp
--8<-- "examples/comments.cpp"
```
Output:
```
--8<-- "examples/comments.output"
```
??? example "Effect of `ignore_trailing_commas` parameter"
The example below demonstrates the effect of the `ignore_trailing_commas` parameter in the `parse()` function.
```cpp
--8<-- "examples/trailing_commas.cpp"
```
Output:
```
--8<-- "examples/trailing_commas.output"
```
## See also
- [accept](accept.md) - check if the input is valid JSON
@ -200,6 +234,7 @@ A UTF-8 byte order mark is silently ignored.
- Overload for contiguous containers (1) added in version 2.0.3.
- Ignoring comments via `ignore_comments` added in version 3.9.0.
- Changed [runtime assertion](../../features/assertions.md) in case of `FILE*` null pointers to exception in version 3.12.0.
- Added `ignore_trailing_commas` in version 3.12.1.
!!! warning "Deprecation"

View File

@ -7,7 +7,8 @@ static bool sax_parse(InputType&& i,
SAX* sax,
input_format_t format = input_format_t::json,
const bool strict = true,
const bool ignore_comments = false);
const bool ignore_comments = false,
const bool ignore_trailing_commas = false);
// (2)
template<class IteratorType, class SAX>
@ -15,7 +16,8 @@ static bool sax_parse(IteratorType first, IteratorType last,
SAX* sax,
input_format_t format = input_format_t::json,
const bool strict = true,
const bool ignore_comments = false);
const bool ignore_comments = false,
const bool ignore_trailing_commas = false);
```
Read from input and generate SAX events
@ -65,6 +67,10 @@ The SAX event lister must follow the interface of [`json_sax`](../json_sax/index
: whether comments should be ignored and treated like whitespace (`#!cpp true`) or yield a parse error
(`#!cpp false`); (optional, `#!cpp false` by default)
`ignore_trailing_commas` (in)
: whether trailing commas in arrays or objects should be ignored and treated like whitespace (`#!cpp true`) or yield a parse error
(`#!cpp false`); (optional, `#!cpp false` by default)
`first` (in)
: iterator to the start of a character range
@ -107,6 +113,7 @@ A UTF-8 byte order mark is silently ignored.
- Added in version 3.2.0.
- Ignoring comments via `ignore_comments` added in version 3.9.0.
- Added `ignore_trailing_commas` in version 3.12.1.
!!! warning "Deprecation"

View File

@ -0,0 +1,31 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
std::string s = R"(
{
// update in 2006: removed Pluto
"planets": ["Mercury", "Venus", "Earth", "Mars",
"Jupiter", "Uranus", "Neptune" /*, "Pluto" */]
}
)";
try
{
json j = json::parse(s);
}
catch (json::exception& e)
{
std::cout << e.what() << std::endl;
}
json j = json::parse(s,
/* callback */ nullptr,
/* allow exceptions */ true,
/* ignore_comments */ true);
std::cout << j.dump(2) << '\n';
}

View File

@ -0,0 +1,12 @@
[json.exception.parse_error.101] parse error at line 3, column 9: syntax error while parsing object key - invalid literal; last read: '<U+000A> {<U+000A> /'; expected string literal
{
"planets": [
"Mercury",
"Venus",
"Earth",
"Mars",
"Jupiter",
"Uranus",
"Neptune"
]
}

View File

@ -0,0 +1,37 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
std::string s = R"(
{
"planets": [
"Mercury",
"Venus",
"Earth",
"Mars",
"Jupiter",
"Uranus",
"Neptune",
]
}
)";
try
{
json j = json::parse(s);
}
catch (json::exception& e)
{
std::cout << e.what() << std::endl;
}
json j = json::parse(s,
/* callback */ nullptr,
/* allow exceptions */ true,
/* ignore_comments */ false,
/* ignore_trailing_commas */ true);
std::cout << j.dump(2) << '\n';
}

View File

@ -0,0 +1,12 @@
[json.exception.parse_error.101] parse error at line 11, column 9: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal
{
"planets": [
"Mercury",
"Venus",
"Earth",
"Mars",
"Jupiter",
"Uranus",
"Neptune"
]
}

View File

@ -11,7 +11,9 @@ This library does not support comments *by default*. It does so for three reason
3. It is dangerous for interoperability if some libraries add comment support while others do not. Please check [The Harmful Consequences of the Robustness Principle](https://tools.ietf.org/html/draft-iab-protocol-maintenance-01) on this.
However, you can pass set parameter `ignore_comments` to `#!c true` in the parse function to ignore `//` or `/* */` comments. Comments will then be treated as whitespace.
However, you can set parameter `ignore_comments` to `#!cpp true` in the [`parse`](../api/basic_json/parse.md) function to ignore `//` or `/* */` comments. Comments will then be treated as whitespace.
For more information, see [JSON With Commas and Comments (JWCC)](https://nigeltao.github.io/blog/2021/json-with-commas-comments.html).
!!! example
@ -28,56 +30,11 @@ However, you can pass set parameter `ignore_comments` to `#!c true` in the parse
When calling `parse` without additional argument, a parse error exception is thrown. If `ignore_comments` is set to `#! true`, the comments are ignored during parsing:
```cpp
#include <iostream>
#include "json.hpp"
using json = nlohmann::json;
int main()
{
std::string s = R"(
{
// update in 2006: removed Pluto
"planets": ["Mercury", "Venus", "Earth", "Mars",
"Jupiter", "Uranus", "Neptune" /*, "Pluto" */]
}
)";
try
{
json j = json::parse(s);
}
catch (json::exception &e)
{
std::cout << e.what() << std::endl;
}
json j = json::parse(s,
/* callback */ nullptr,
/* allow exceptions */ true,
/* ignore_comments */ true);
std::cout << j.dump(2) << '\n';
}
--8<-- "examples/comments.cpp"
```
Output:
```
[json.exception.parse_error.101] parse error at line 3, column 9:
syntax error while parsing object key - invalid literal;
last read: '<U+000A> {<U+000A> /'; expected string literal
```
```json
{
"planets": [
"Mercury",
"Venus",
"Earth",
"Mars",
"Jupiter",
"Uranus",
"Neptune"
]
}
--8<-- "examples/comments.output"
```

View File

@ -0,0 +1,39 @@
# Trailing Commas
Like [comments](comments.md), this library does not support trailing commas in arrays and objects *by default*.
You can set parameter `ignore_trailing_commas` to `#!cpp true` in the [`parse`](../api/basic_json/parse.md) function to allow trailing commas in arrays and objects. Note that a single comma as the only content of the array or object (`[,]` or `{,}`) is not allowed, and multiple trailing commas (`[1,,]`) are not allowed either.
This library does not add trailing commas when serializing JSON data.
For more information, see [JSON With Commas and Comments (JWCC)](https://nigeltao.github.io/blog/2021/json-with-commas-comments.html).
!!! example
Consider the following JSON with trailing commas.
```json
{
"planets": [
"Mercury",
"Venus",
"Earth",
"Mars",
"Jupiter",
"Uranus",
"Neptune",
]
}
```
When calling `parse` without additional argument, a parse error exception is thrown. If `ignore_trailing_commas` is set to `#! true`, the trailing commas are ignored during parsing:
```cpp
--8<-- "examples/trailing_commas.cpp"
```
Output:
```
--8<-- "examples/trailing_commas.output"
```

View File

@ -67,6 +67,7 @@ nav:
- features/binary_formats/ubjson.md
- features/binary_values.md
- features/comments.md
- features/trailing_commas.md
- Element Access:
- features/element_access/index.md
- features/element_access/unchecked_access.md

View File

@ -71,10 +71,12 @@ class parser
explicit parser(InputAdapterType&& adapter,
parser_callback_t<BasicJsonType> cb = nullptr,
const bool allow_exceptions_ = true,
const bool skip_comments = false)
const bool ignore_comments = false,
const bool ignore_trailing_commas_ = false)
: callback(std::move(cb))
, m_lexer(std::move(adapter), skip_comments)
, m_lexer(std::move(adapter), ignore_comments)
, allow_exceptions(allow_exceptions_)
, ignore_trailing_commas(ignore_trailing_commas_)
{
// read first token
get_token();
@ -384,12 +386,18 @@ class parser
if (states.back()) // array
{
// comma -> next value
// or end of array (ignore_trailing_commas = true)
if (get_token() == token_type::value_separator)
{
// parse a new value
get_token();
// if ignore_trailing_commas and last_token is ], we can continue to "closing ]"
if (!(ignore_trailing_commas && last_token == token_type::end_array))
{
continue;
}
}
// closing ]
if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array))
@ -417,10 +425,16 @@ class parser
// states.back() is false -> object
// comma -> next value
// or end of object (ignore_trailing_commas = true)
if (get_token() == token_type::value_separator)
{
get_token();
// if ignore_trailing_commas and last_token is }, we can continue to "closing }"
if (!(ignore_trailing_commas && last_token == token_type::end_object))
{
// parse key
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string))
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
@ -444,6 +458,7 @@ class parser
get_token();
continue;
}
}
// closing }
if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object))
@ -513,6 +528,8 @@ class parser
lexer_t m_lexer;
/// whether to throw exceptions in case of errors
const bool allow_exceptions = true;
/// whether trailing commas in objects and arrays should be ignored (true) or signaled as errors (false)
const bool ignore_trailing_commas = false;
};
} // namespace detail

View File

@ -134,11 +134,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
InputAdapterType adapter,
detail::parser_callback_t<basic_json>cb = nullptr,
const bool allow_exceptions = true,
const bool ignore_comments = false
const bool ignore_comments = false,
const bool ignore_trailing_commas = false
)
{
return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter),
std::move(cb), allow_exceptions, ignore_comments);
std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas);
}
private:
@ -4045,10 +4046,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static basic_json parse(InputType&& i,
parser_callback_t cb = nullptr,
const bool allow_exceptions = true,
const bool ignore_comments = false)
const bool ignore_comments = false,
const bool ignore_trailing_commas = false)
{
basic_json result;
parser(detail::input_adapter(std::forward<InputType>(i)), std::move(cb), allow_exceptions, ignore_comments).parse(true, result); // cppcheck-suppress[accessMoved,accessForwarded]
parser(detail::input_adapter(std::forward<InputType>(i)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved,accessForwarded]
return result;
}
@ -4060,10 +4062,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
IteratorType last,
parser_callback_t cb = nullptr,
const bool allow_exceptions = true,
const bool ignore_comments = false)
const bool ignore_comments = false,
const bool ignore_trailing_commas = false)
{
basic_json result;
parser(detail::input_adapter(std::move(first), std::move(last)), std::move(cb), allow_exceptions, ignore_comments).parse(true, result); // cppcheck-suppress[accessMoved]
parser(detail::input_adapter(std::move(first), std::move(last)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved]
return result;
}
@ -4072,10 +4075,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static basic_json parse(detail::span_input_adapter&& i,
parser_callback_t cb = nullptr,
const bool allow_exceptions = true,
const bool ignore_comments = false)
const bool ignore_comments = false,
const bool ignore_trailing_commas = false)
{
basic_json result;
parser(i.get(), std::move(cb), allow_exceptions, ignore_comments).parse(true, result); // cppcheck-suppress[accessMoved]
parser(i.get(), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved]
return result;
}
@ -4083,26 +4087,29 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/accept/
template<typename InputType>
static bool accept(InputType&& i,
const bool ignore_comments = false)
const bool ignore_comments = false,
const bool ignore_trailing_commas = false)
{
return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments).accept(true);
return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);
}
/// @brief check if the input is valid JSON
/// @sa https://json.nlohmann.me/api/basic_json/accept/
template<typename IteratorType>
static bool accept(IteratorType first, IteratorType last,
const bool ignore_comments = false)
const bool ignore_comments = false,
const bool ignore_trailing_commas = false)
{
return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true);
return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);
}
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len))
static bool accept(detail::span_input_adapter&& i,
const bool ignore_comments = false)
const bool ignore_comments = false,
const bool ignore_trailing_commas = false)
{
return parser(i.get(), nullptr, false, ignore_comments).accept(true);
return parser(i.get(), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);
}
/// @brief generate SAX events
@ -4112,11 +4119,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static bool sax_parse(InputType&& i, SAX* sax,
input_format_t format = input_format_t::json,
const bool strict = true,
const bool ignore_comments = false)
const bool ignore_comments = false,
const bool ignore_trailing_commas = false)
{
auto ia = detail::input_adapter(std::forward<InputType>(i));
return format == input_format_t::json
? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
}
@ -4127,11 +4135,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static bool sax_parse(IteratorType first, IteratorType last, SAX* sax,
input_format_t format = input_format_t::json,
const bool strict = true,
const bool ignore_comments = false)
const bool ignore_comments = false,
const bool ignore_trailing_commas = false)
{
auto ia = detail::input_adapter(std::move(first), std::move(last));
return format == input_format_t::json
? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
}
@ -4146,12 +4155,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static bool sax_parse(detail::span_input_adapter&& i, SAX* sax,
input_format_t format = input_format_t::json,
const bool strict = true,
const bool ignore_comments = false)
const bool ignore_comments = false,
const bool ignore_trailing_commas = false)
{
auto ia = i.get();
return format == input_format_t::json
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
}

View File

@ -12987,10 +12987,12 @@ class parser
explicit parser(InputAdapterType&& adapter,
parser_callback_t<BasicJsonType> cb = nullptr,
const bool allow_exceptions_ = true,
const bool skip_comments = false)
const bool ignore_comments = false,
const bool ignore_trailing_commas_ = false)
: callback(std::move(cb))
, m_lexer(std::move(adapter), skip_comments)
, m_lexer(std::move(adapter), ignore_comments)
, allow_exceptions(allow_exceptions_)
, ignore_trailing_commas(ignore_trailing_commas_)
{
// read first token
get_token();
@ -13300,12 +13302,18 @@ class parser
if (states.back()) // array
{
// comma -> next value
// or end of array (ignore_trailing_commas = true)
if (get_token() == token_type::value_separator)
{
// parse a new value
get_token();
// if ignore_trailing_commas and last_token is ], we can continue to "closing ]"
if (!(ignore_trailing_commas && last_token == token_type::end_array))
{
continue;
}
}
// closing ]
if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array))
@ -13333,10 +13341,16 @@ class parser
// states.back() is false -> object
// comma -> next value
// or end of object (ignore_trailing_commas = true)
if (get_token() == token_type::value_separator)
{
get_token();
// if ignore_trailing_commas and last_token is }, we can continue to "closing }"
if (!(ignore_trailing_commas && last_token == token_type::end_object))
{
// parse key
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string))
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
@ -13360,6 +13374,7 @@ class parser
get_token();
continue;
}
}
// closing }
if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object))
@ -13429,6 +13444,8 @@ class parser
lexer_t m_lexer;
/// whether to throw exceptions in case of errors
const bool allow_exceptions = true;
/// whether trailing commas in objects and arrays should be ignored (true) or signaled as errors (false)
const bool ignore_trailing_commas = false;
};
} // namespace detail
@ -20219,11 +20236,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
InputAdapterType adapter,
detail::parser_callback_t<basic_json>cb = nullptr,
const bool allow_exceptions = true,
const bool ignore_comments = false
const bool ignore_comments = false,
const bool ignore_trailing_commas = false
)
{
return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter),
std::move(cb), allow_exceptions, ignore_comments);
std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas);
}
private:
@ -24130,10 +24148,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static basic_json parse(InputType&& i,
parser_callback_t cb = nullptr,
const bool allow_exceptions = true,
const bool ignore_comments = false)
const bool ignore_comments = false,
const bool ignore_trailing_commas = false)
{
basic_json result;
parser(detail::input_adapter(std::forward<InputType>(i)), std::move(cb), allow_exceptions, ignore_comments).parse(true, result); // cppcheck-suppress[accessMoved,accessForwarded]
parser(detail::input_adapter(std::forward<InputType>(i)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved,accessForwarded]
return result;
}
@ -24145,10 +24164,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
IteratorType last,
parser_callback_t cb = nullptr,
const bool allow_exceptions = true,
const bool ignore_comments = false)
const bool ignore_comments = false,
const bool ignore_trailing_commas = false)
{
basic_json result;
parser(detail::input_adapter(std::move(first), std::move(last)), std::move(cb), allow_exceptions, ignore_comments).parse(true, result); // cppcheck-suppress[accessMoved]
parser(detail::input_adapter(std::move(first), std::move(last)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved]
return result;
}
@ -24157,10 +24177,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static basic_json parse(detail::span_input_adapter&& i,
parser_callback_t cb = nullptr,
const bool allow_exceptions = true,
const bool ignore_comments = false)
const bool ignore_comments = false,
const bool ignore_trailing_commas = false)
{
basic_json result;
parser(i.get(), std::move(cb), allow_exceptions, ignore_comments).parse(true, result); // cppcheck-suppress[accessMoved]
parser(i.get(), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved]
return result;
}
@ -24168,26 +24189,29 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/accept/
template<typename InputType>
static bool accept(InputType&& i,
const bool ignore_comments = false)
const bool ignore_comments = false,
const bool ignore_trailing_commas = false)
{
return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments).accept(true);
return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);
}
/// @brief check if the input is valid JSON
/// @sa https://json.nlohmann.me/api/basic_json/accept/
template<typename IteratorType>
static bool accept(IteratorType first, IteratorType last,
const bool ignore_comments = false)
const bool ignore_comments = false,
const bool ignore_trailing_commas = false)
{
return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true);
return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);
}
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len))
static bool accept(detail::span_input_adapter&& i,
const bool ignore_comments = false)
const bool ignore_comments = false,
const bool ignore_trailing_commas = false)
{
return parser(i.get(), nullptr, false, ignore_comments).accept(true);
return parser(i.get(), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);
}
/// @brief generate SAX events
@ -24197,11 +24221,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static bool sax_parse(InputType&& i, SAX* sax,
input_format_t format = input_format_t::json,
const bool strict = true,
const bool ignore_comments = false)
const bool ignore_comments = false,
const bool ignore_trailing_commas = false)
{
auto ia = detail::input_adapter(std::forward<InputType>(i));
return format == input_format_t::json
? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
}
@ -24212,11 +24237,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static bool sax_parse(IteratorType first, IteratorType last, SAX* sax,
input_format_t format = input_format_t::json,
const bool strict = true,
const bool ignore_comments = false)
const bool ignore_comments = false,
const bool ignore_trailing_commas = false)
{
auto ia = detail::input_adapter(std::move(first), std::move(last));
return format == input_format_t::json
? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
}
@ -24231,12 +24257,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static bool sax_parse(detail::span_input_adapter&& i, SAX* sax,
input_format_t format = input_format_t::json,
const bool strict = true,
const bool ignore_comments = false)
const bool ignore_comments = false,
const bool ignore_trailing_commas = false)
{
auto ia = i.get();
return format == input_format_t::json
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
}

View File

@ -206,6 +206,7 @@ class SaxCountdown : public nlohmann::json::json_sax_t
json parser_helper(const std::string& s);
bool accept_helper(const std::string& s);
void comments_helper(const std::string& s);
void trailing_comma_helper(const std::string& s);
json parser_helper(const std::string& s)
{
@ -225,6 +226,8 @@ json parser_helper(const std::string& s)
comments_helper(s);
trailing_comma_helper(s);
return j;
}
@ -259,10 +262,11 @@ bool accept_helper(const std::string& s)
// 6. check if this approach came to the same result
CHECK(ok_noexcept == ok_noexcept_cb);
// 7. check if comments are properly ignored
// 7. check if comments or trailing commas are properly ignored
if (ok_accept)
{
comments_helper(s);
trailing_comma_helper(s);
}
// 8. return result
@ -302,6 +306,38 @@ void comments_helper(const std::string& s)
}
}
void trailing_comma_helper(const std::string& s)
{
json _;
// parse/accept with default parser
CHECK_NOTHROW(_ = json::parse(s));
CHECK(json::accept(s));
// parse/accept while allowing trailing commas
CHECK_NOTHROW(_ = json::parse(s, nullptr, false, false, true));
CHECK(json::accept(s, false, true));
// note: [,] and {,} are not allowed
if (s.size() > 1 && (s.back() == ']' || s.back() == '}') && !_.empty())
{
std::vector<std::string> json_with_trailing_commas;
json_with_trailing_commas.push_back(s.substr(0, s.size() - 1) + " ," + s.back());
json_with_trailing_commas.push_back(s.substr(0, s.size() - 1) + "," + s.back());
json_with_trailing_commas.push_back(s.substr(0, s.size() - 1) + ", " + s.back());
for (const auto& json_with_trailing_comma : json_with_trailing_commas)
{
CAPTURE(json_with_trailing_comma)
CHECK_THROWS_AS(_ = json::parse(json_with_trailing_comma), json::parse_error);
CHECK(!json::accept(json_with_trailing_comma));
CHECK_NOTHROW(_ = json::parse(json_with_trailing_comma, nullptr, true, false, true));
CHECK(json::accept(json_with_trailing_comma, false, true));
}
}
}
} // namespace
TEST_CASE("parser class")