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:
12
README.md
12
README.md
@ -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
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
31
docs/mkdocs/docs/examples/comments.cpp
Normal file
31
docs/mkdocs/docs/examples/comments.cpp
Normal 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';
|
||||
}
|
12
docs/mkdocs/docs/examples/comments.output
Normal file
12
docs/mkdocs/docs/examples/comments.output
Normal 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"
|
||||
]
|
||||
}
|
37
docs/mkdocs/docs/examples/trailing_commas.cpp
Normal file
37
docs/mkdocs/docs/examples/trailing_commas.cpp
Normal 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';
|
||||
}
|
12
docs/mkdocs/docs/examples/trailing_commas.output
Normal file
12
docs/mkdocs/docs/examples/trailing_commas.output
Normal 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"
|
||||
]
|
||||
}
|
@ -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"
|
||||
```
|
||||
|
39
docs/mkdocs/docs/features/trailing_commas.md
Normal file
39
docs/mkdocs/docs/features/trailing_commas.md
Normal 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"
|
||||
```
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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")
|
||||
|
Reference in New Issue
Block a user