diff --git a/.clang-format b/.clang-format deleted file mode 100644 index 5b9e3fd530..0000000000 --- a/.clang-format +++ /dev/null @@ -1,84 +0,0 @@ -#AccessModifierOffset: 2 -AlignAfterOpenBracket: Align -AlignConsecutiveAssignments: false -#AlignConsecutiveBitFields: false -AlignConsecutiveDeclarations: false -AlignConsecutiveMacros: false -AlignEscapedNewlines: Right -#AlignOperands: AlignAfterOperator -AlignTrailingComments: true -AllowAllArgumentsOnNextLine: false -AllowAllConstructorInitializersOnNextLine: false -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortBlocksOnASingleLine: Empty -AllowShortCaseLabelsOnASingleLine: false -#AllowShortEnumsOnASingleLine: true -AllowShortFunctionsOnASingleLine: Empty -AllowShortIfStatementsOnASingleLine: Never -AllowShortLambdasOnASingleLine: Empty -AllowShortLoopsOnASingleLine: false -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: Yes -BinPackArguments: false -BinPackParameters: false -#BitFieldColonSpacing: Both -BreakBeforeBraces: Custom # or Allman -BraceWrapping: - AfterCaseLabel: true - AfterClass: true - AfterControlStatement: Always - AfterEnum: true - AfterFunction: true - AfterNamespace: false - AfterStruct: true - AfterUnion: true - AfterExternBlock: false - BeforeCatch: true - BeforeElse: true - #BeforeLambdaBody: false - #BeforeWhile: false - SplitEmptyFunction: false - SplitEmptyRecord: false - SplitEmptyNamespace: false -BreakBeforeTernaryOperators: true -BreakConstructorInitializers: BeforeComma -BreakStringLiterals: false -ColumnLimit: 0 -CompactNamespaces: false -ConstructorInitializerIndentWidth: 2 -Cpp11BracedListStyle: true -PointerAlignment: Left -FixNamespaceComments: true -IncludeBlocks: Preserve -#IndentCaseBlocks: false -IndentCaseLabels: true -IndentGotoLabels: false -IndentPPDirectives: BeforeHash -IndentWidth: 4 -KeepEmptyLinesAtTheStartOfBlocks: false -MaxEmptyLinesToKeep: 1 -NamespaceIndentation: None -ReflowComments: false -SortIncludes: true -SortUsingDeclarations: true -SpaceAfterCStyleCast: false -SpaceAfterLogicalNot: false -SpaceAfterTemplateKeyword: false -SpaceBeforeAssignmentOperators: true -SpaceBeforeCpp11BracedList: false -SpaceBeforeParens: ControlStatements -SpaceBeforeRangeBasedForLoopColon: true -SpaceBeforeSquareBrackets: false -SpaceInEmptyBlock: false -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 2 -SpacesInAngles: false -SpacesInCStyleCastParentheses: false -SpacesInConditionalStatement: false -SpacesInContainerLiterals: false -SpacesInParentheses: false -SpacesInSquareBrackets: false -Standard: c++11 -TabWidth: 4 -UseTab: Never diff --git a/.clang-tidy b/.clang-tidy index 1d1e31f688..29b3e93024 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,10 +1,20 @@ +# TODO: The first three checks are only removed to get the CI going. They have to be addressed at some point. +# TODO: portability-avoid-pragma-once: should be fixed eventually + Checks: '*, + + -portability-template-virtual-member-function, + -bugprone-use-after-move, + -hicpp-invalid-access-moved, + -altera-id-dependent-backward-branch, -altera-struct-pack-align, -altera-unroll-loops, -android-cloexec-fopen, + -boost-use-ranges, -bugprone-easily-swappable-parameters, -cert-err58-cpp, + -clang-analyzer-webkit.NoUncountedMemberChecker, -concurrency-mt-unsafe, -cppcoreguidelines-avoid-const-or-ref-data-members, -cppcoreguidelines-avoid-do-while, @@ -12,6 +22,7 @@ Checks: '*, -cppcoreguidelines-avoid-magic-numbers, -cppcoreguidelines-avoid-non-const-global-variables, -cppcoreguidelines-macro-usage, + -cppcoreguidelines-pro-bounds-avoid-unchecked-container-access, -cppcoreguidelines-pro-bounds-array-to-pointer-decay, -cppcoreguidelines-pro-bounds-constant-array-index, -cppcoreguidelines-pro-bounds-pointer-arithmetic, @@ -24,6 +35,7 @@ Checks: '*, -fuchsia-overloaded-operator, -google-explicit-constructor, -google-readability-function-size, + -google-runtime-float, -google-runtime-int, -google-runtime-references, -hicpp-avoid-goto, @@ -35,6 +47,8 @@ Checks: '*, -hicpp-uppercase-literal-suffix, -llvm-header-guard, -llvm-include-order, + -llvm-prefer-static-over-anonymous-namespace, + -llvm-use-ranges, -llvmlibc-*, -misc-use-anonymous-namespace, -misc-confusable-identifiers, @@ -44,17 +58,22 @@ Checks: '*, -modernize-concat-nested-namespaces, -modernize-type-traits, -modernize-use-constraints, + -modernize-use-designated-initializers, -modernize-use-nodiscard, + -modernize-use-ranges, -modernize-use-std-numbers, -modernize-use-trailing-return-type, -performance-enum-size, + -portability-avoid-pragma-once, -readability-function-cognitive-complexity, -readability-function-size, -readability-identifier-length, -readability-magic-numbers, -readability-redundant-access-specifiers, + -readability-redundant-parentheses, -readability-simplify-boolean-expr, - -readability-uppercase-literal-suffix' + -readability-uppercase-literal-suffix, + -readability-use-concise-preprocessor-directives' CheckOptions: - key: hicpp-special-member-functions.AllowSoleDefaultDtor diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index 770b8173e1..30eb5784ce 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -2,45 +2,131 @@ ## Our Pledge -In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. ## Our Standards -Examples of behavior that contributes to creating a positive environment include: +Examples of behavior that contributes to a positive environment for our +community include: -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community -Examples of unacceptable behavior by participants include: +Examples of unacceptable behavior include: -* The use of sexualized language or imagery and unwelcome sexual attention or advances -* Trolling, insulting/derogatory comments, and personal or political attacks +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment -* Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional setting +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting -## Our Responsibilities +## Enforcement Responsibilities -Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. ## Scope -This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. ## Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at mail@nlohmann.me. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[mail@nlohmann.me](mailto:mail@nlohmann.me). +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. -Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index dc98fd8783..b0f65230cc 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,69 +1,225 @@ -# How to contribute +# Contribution Guidelines -This project started as a little excuse to exercise some of the cool new C++11 features. Over time, people actually started to use the JSON library (yey!) and started to help improve it by proposing features, finding bugs, or even fixing my mistakes. I am really [thankful](https://github.com/nlohmann/json/blob/master/README.md#thanks) for this and try to keep track of all the helpers. +Thank you for your interest in contributing to this project! What began as an exercise to explore the exciting features +of C++11 has evolved into a [widely used](https://json.nlohmann.me/home/customers/) JSON library. I truly appreciate all +the contributions from the community, whether it's proposing features, identifying bugs, or fixing mistakes! To ensure +that our collaboration is efficient and effective, please follow these guidelines. -To make it as easy as possible for you to contribute and for me to keep an overview, here are a few guidelines which should help us avoid all kinds of unnecessary work or disappointment. And of course, this document is subject to discussion, so please [create an issue](https://github.com/nlohmann/json/issues/new/choose) or a pull request if you find a way to improve it! +Feel free to discuss or suggest improvements to this document +[by submitting a pull request](https://github.com/nlohmann/json/edit/develop/.github/CONTRIBUTING.md). -## Private reports +## Ways to Contribute -Usually, all issues are tracked publicly on [GitHub](https://github.com/nlohmann/json/issues). If you want to make a private report (e.g., for a vulnerability or to attach an example that is not meant to be published), please send an email to . +There are multiple ways to contribute. -## Prerequisites +### Reporting an issue -Please [create an issue](https://github.com/nlohmann/json/issues/new/choose), assuming one does not already exist, and describe your concern. Note you need a [GitHub account](https://github.com/signup/free) for this. - -## Describe your issue +Please [create an issue](https://github.com/nlohmann/json/issues/new/choose), assuming one does not already exist, and +describe your concern. Note you need a [GitHub account](https://github.com/signup/free) for this. Clearly describe the issue: -- If it is a bug, please describe how to **reproduce** it. If possible, attach a complete example which demonstrates the error. Please also state what you **expected** to happen instead of the error. -- If you propose a change or addition, try to give an **example** how the improved code could look like or how to use it. -- If you found a compilation error, please tell us which **compiler** (version and operating system) you used and paste the (relevant part of) the error messages to the ticket. +- If it is a bug, please describe how to **reproduce** it. If possible, attach a _complete example_ which demonstrates + the error. Please also state what you **expected** to happen instead of the error. +- If you propose a change or addition, try to give an **example** what the improved code could look like or how to use + it. +- If you found a compilation error, please tell us which **compiler** (version and operating system) you used and paste + the (relevant part of) the error messages to the ticket. + +Please stick to the provided issue template +[bug report](https://github.com/nlohmann/json/blob/develop/.github/ISSUE_TEMPLATE/bug.yaml) if possible. + +### Reporting a security vulnerability + +You can report a security vulnerability according to our +[security policy](https://github.com/nlohmann/json/security/policy). + +### Discussing a new feature + +For questions, feature or support requests, please +[open a discussion](https://github.com/nlohmann/json/discussions/new). If you find a proposed answer satisfactory, +please use the "Mark as answer" button to make it easier for readers to see what helped and for the community to filter +for open questions. + +### Proposing a fix or an improvement + +Join an ongoing discussion or comment on an existing issue before starting to code. This can help to avoid duplicate +efforts or other frustration during the later review. + +Create a [pull request](https://github.com/nlohmann/json/pulls?q=sort%3Aupdated-desc+is%3Apr+is%3Aopen) against the +`develop` branch and follow the pull request template. In particular, + +- describe the changes in detail, both the what and why, +- reference existing issues where applicable, +- add tests to maintain 100% test coverage, +- update the documentation as needed, and +- ensure the source code is amalgamated. + +We describe all points in detail below. + +All contributions (including pull requests) must agree to the +[Developer Certificate of Origin (DCO) version 1.1](https://developercertificate.org). This is exactly the same one +created and used by the Linux kernel developers and posted on http://developercertificate.org/. This is a developer's +certification that he or she has the right to submit the patch for inclusion into the project. + +## How to... + +### Describe your changes + +This library is primarily maintained as a spare-time project. As such, I cannot make any guarantee how quickly changes +are merged and released. Therefore, it is very important to make the review as smooth as possible by explaining not only +_what_ you changed, but _why_. This rationale can be very valuable down the road when improvements or bugs are discussed +years later. + +### Reference an existing issue + +[Link a pull request to an issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/linking-a-pull-request-to-an-issue) +to clarify that a fix is forthcoming and which issue can be closed after merging. Only a few cases (e.g., fixing typos) +do not require prior discussions. + +### Write tests + +The library has an extensive test suite that currently covers [100 %](https://coveralls.io/github/nlohmann/json) of the +library's code. These tests are crucial to maintain API stability and give future contributors confidence that they do +not accidentally break things. As Titus Winters aptly put it: + +> If you liked it, you should have put a test on it. + +#### Run the tests -Please stick to the provided issue template ([bug report](https://github.com/nlohmann/json/blob/develop/.github/ISSUE_TEMPLATE/bug.yml) if possible. For questions, feature or support requests, please [open a discussion](https://github.com/nlohmann/json/discussions/new). +First, ensure the test suite runs before making any changes: -## Files to change +```sh +$ cmake -S. -B build +$ cmake --build build -j 10 +$ ctest --test-dir build -j 10 +``` -:exclamation: Before you make any changes, note the single-header files [`single_include/nlohmann/json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp) and [`single_include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_fwd.hpp) are **generated** from the source files in the [`include/nlohmann` directory](https://github.com/nlohmann/json/tree/develop/include/nlohmann). Please **do not** edit the files `single_include/nlohmann/json.hpp` and `single_include/nlohmann/json_fwd.hpp` directly, but change the `include/nlohmann` sources and regenerate the files by executing `make amalgamate`. +The test suite should report: -To make changes, you need to edit the following files: +``` +100% tests passed, 0 tests failed out of 98 +``` -1. [`include/nlohmann/*`](https://github.com/nlohmann/json/tree/develop/include/nlohmann) - These files are the sources of the library. Before testing or creating a pull request, execute `make amalgamate` to regenerate `single_include/nlohmann/json.hpp` and `single_include/nlohmann/json_fwd.hpp`. +#### Add tests -2. [`tests/src/unit-*.cpp`](https://github.com/nlohmann/json/tree/develop/tests/src) - These files contain the [doctest](https://github.com/onqtam/doctest) unit tests which currently cover [100 %](https://coveralls.io/github/nlohmann/json) of the library's code. Before creating a pull request, execute `make pretty` to make sure that the style is correct, as this will be checked by the CI. +The tests are located in [`tests/src/unit-*.cpp`](https://github.com/nlohmann/json/tree/develop/tests/src) and contain +[doctest assertions](https://github.com/doctest/doctest/blob/master/doc/markdown/assertions.md) like `CHECK`. The tests +are structured along the features of the library or the nature of the tests. Usually, it should be clear from the +context which existing file needs to be extended, and only very few cases require creating new test files. - If you add or change a feature, please also add a unit test to this file. The unit tests can be compiled and executed with +When fixing a bug, edit `unit-regression2.cpp` and add a section referencing the fixed issue. - ```sh - $ mkdir build - $ cd build - $ cmake .. - $ cmake --build . - $ ctest - ``` +#### Exceptions - The test cases are also executed with several different compilers on [Travis](https://travis-ci.org/nlohmann/json) once you open a pull request. +When you test exceptions, please use `CHECK_THROWS_WITH_AS` which also takes the `what()` argument of the thrown +exception into account. +#### Coverage -## Note +If test coverage decreases, an automatic warning comment will be posted on the pull request. You can access a code +coverage report as an artifact to the “Ubuntu” workflow. -- If you open a pull request, the code will be automatically tested with [Valgrind](http://valgrind.org)'s Memcheck tool to detect memory leaks. Please be aware that the execution with Valgrind _may_ in rare cases yield different behavior than running the code directly. This can result in failing unit tests which run successfully without Valgrind. -- There is a Makefile target `make pretty` which runs [Artistic Style](http://astyle.sourceforge.net) to fix indentation. If possible, run it before opening the pull request. Otherwise, we shall run it afterward. +### Update the documentation -## Please don't +The [main documentation](https://json.nlohmann.me) of the library is generated from the files +[`docs/mkdocs/docs`](https://github.com/nlohmann/json/blob/develop/docs/mkdocs/docs). This folder contains dedicated +pages for [certain features](https://github.com/nlohmann/json/tree/develop/docs/mkdocs/docs/features), a list of +[all exceptions](https://github.com/nlohmann/json/blob/develop/docs/mkdocs/docs/home/exceptions.md), and +[extensive API documentation](https://github.com/nlohmann/json/tree/develop/docs/mkdocs/docs/api) with details on every +public API function. -- The C++11 support varies between different **compilers** and versions. Please note the [list of supported compilers](https://github.com/nlohmann/json/blob/master/README.md#supported-compilers). Some compilers like GCC 4.7 (and earlier), Clang 3.3 (and earlier), or Microsoft Visual Studio 13.0 and earlier are known not to work due to missing or incomplete C++11 support. Please refrain from proposing changes that work around these compiler's limitations with `#ifdef`s or other means. -- Specifically, I am aware of compilation problems with **Microsoft Visual Studio** (there even is an [issue label](https://github.com/nlohmann/json/issues?utf8=✓&q=label%3A%22visual+studio%22+) for this kind of bug). I understand that even in 2016, complete C++11 support isn't there yet. But please also understand that I do not want to drop features or uglify the code just to make Microsoft's sub-standard compiler happy. The past has shown that there are ways to express the functionality such that the code compiles with the most recent MSVC - unfortunately, this is not the main objective of the project. -- Please refrain from proposing changes that would **break [JSON](https://json.org) conformance**. If you propose a conformant extension of JSON to be supported by the library, please motivate this extension. - - We shall not extend the library to **support comments**. There is quite some [controversy](https://www.reddit.com/r/programming/comments/4v6chu/why_json_doesnt_support_comments_douglas_crockford/) around this topic, and there were quite some [issues](https://github.com/nlohmann/json/issues/376) on this. We believe that JSON is fine without comments. - - We do not preserve the **insertion order of object elements**. The [JSON standard](https://tools.ietf.org/html/rfc8259.html) defines objects as "an unordered collection of zero or more name/value pairs". To this end, this library does not preserve insertion order of name/value pairs. (In fact, keys will be traversed in alphabetical order as `std::map` with `std::less` is used by default.) Note this behavior conforms to the standard, and we shall not change it to any other order. If you do want to preserve the insertion order, you can specialize the object type with containers like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map). +Build the documentation locally using: -- Please do not open pull requests that address **multiple issues**. +```shell +make install_venv -C docs/mkdocs +make serve -C docs/mkdocs +``` + +The documentation will then be available at . See the documentation of +[mkdocs](https://www.mkdocs.org) and [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) for more +information. + +### Amalgamate the source code + +The single-header files +[`single_include/nlohmann/json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp) and +[`single_include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_fwd.hpp) +are **generated** from the source files in the +[`include/nlohmann` directory](https://github.com/nlohmann/json/tree/develop/include/nlohmann). **Do not** edit the +files directly; instead, modify the include/nlohmann sources and regenerate the files by executing: + +```shell +make amalgamate +``` + +Running `make amalgamate` will also apply automatic formatting to the source files using +[`Artistic Style`](https://astyle.sourceforge.net/). This formatting may modify your source files in-place. Be certain to review and commit any changes to avoid unintended formatting diffs in commits. + +## Recommended documentation + +- The library’s [README file](https://github.com/nlohmann/json/blob/master/README.md) is an excellent starting point to + understand its functionality. +- The [documentation page](https://json.nlohmann.me) is the reference documentation of the library. +- [RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259) is the reference for the JavaScript Object Notation (JSON) + Data Interchange Format. + +## Please don't... + +Certain contributions are not helpful. + +### Break the public API + +We take pride in the library being used by +[numerous customers across various industries](https://json.nlohmann.me/home/customers/). They all rely on the +guarantees provided by [semantic versioning](https://semver.org). Please do not change the library such that the public +API of the 3.x.y version is broken. This includes: + +- Changing function signatures (altering parameter types, return types, number of parameters) or changing the const-ness + of member functions. +- Removing functions. +- Renaming functions or classes. +- Changing exception handling. +- Changing exception ids. +- Changing access specifiers. +- Changing default arguments. + +Although these guidelines may seem restrictive, they are essential for maintaining the library’s utility. + +Breaking changes may be introduced when they are guarded with a feature macro such as +[`JSON_USE_IMPLICIT_CONVERSIONS`](https://json.nlohmann.me/api/macros/json_use_implicit_conversions/) which allows +selectively changing the behavior of the library. In next steps, the current behavior can then be deprecated. Using +feature macros then allows users to test their code against the library in the next major release. + +### Break C++11 language conformance + +This library is designed to work with C++11 and later. This means that any +[supported C++11 compiler](https://github.com/nlohmann/json/blob/master/README.md#supported-compilers) should compile +the library without problems. Some compilers like GCC 4.7 (and earlier), Clang 3.3 (and earlier), or Microsoft Visual +Studio 13.0 and earlier are known not to work due to missing or incomplete C++11 support. + +Please do not add features that do not work with the mentioned supported compilers. Please guard features from C++14 and +later against the respective [`JSON_HAS_CPP_14`](https://json.nlohmann.me/api/macros/json_has_cpp_11/) macros. + +### Break JSON conformance + +Please refrain from proposing changes that would **break [JSON](https://datatracker.ietf.org/doc/html/rfc8259) +conformance**. If you propose a conformant extension of JSON to be supported by the library, please motivate this +extension. ## Wanted -The following areas really need contribution: +The following areas really need contribution and are always welcomed: + +- Extending the **continuous integration** toward more exotic compilers such as Android NDK, Intel's Compiler, or the + bleeding-edge versions Clang. +- Improving the efficiency of the **JSON parser**. The current parser is implemented as a naive recursive descent parser + with hand-coded string handling. More sophisticated approaches like LALR parsers would be really appreciated. That + said, parser generators like Bison or ANTLR do not play nice with single-header files -- I really would like to keep + the parser inside the `json.hpp` header, and I am not aware of approaches similar to [`re2c`](http://re2c.org) for + parsing. +- Extending and updating existing **benchmarks** to include (the most recent version of) this library. Though efficiency + is not everything, speed and memory consumption are very important characteristics for C++ developers, so having + proper comparisons would be interesting. -- Extending the **continuous integration** toward more exotic compilers such as Android NDK, Intel's Compiler, or the bleeding-edge versions Clang. -- Improving the efficiency of the **JSON parser**. The current parser is implemented as a naive recursive descent parser with hand coded string handling. More sophisticated approaches like LALR parsers would be really appreciated. That said, parser generators like Bison or ANTLR do not play nice with single-header files -- I really would like to keep the parser inside the `json.hpp` header, and I am not aware of approaches similar to [`re2c`](http://re2c.org) for parsing. -- Extending and updating existing **benchmarks** to include (the most recent version of) this library. Though efficiency is not everything, speed and memory consumption are very important characteristics for C++ developers, so having proper comparisons would be interesting. +We look forward to your contributions and collaboration to enhance the library! diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index a6c972e819..27bd6810a9 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,2 +1,2 @@ github: nlohmann -custom: http://paypal.me/nlohmann +custom: https://paypal.me/nlohmann diff --git a/.github/ISSUE_TEMPLATE/bug.yaml b/.github/ISSUE_TEMPLATE/bug.yaml index f7acdf18b2..ed426675dd 100644 --- a/.github/ISSUE_TEMPLATE/bug.yaml +++ b/.github/ISSUE_TEMPLATE/bug.yaml @@ -13,7 +13,9 @@ body: Note that this form is for bug reports only. Please [open a discussion](https://github.com/nlohmann/json/discussions/new) - for questions, feature requests, or support requests + for questions, feature requests, or support requests. + **Please check the [FAQ](https://json.nlohmann.me/home/faq/) before + reporting an issue.** Common questions are answered there. - type: textarea id: summary attributes: @@ -76,7 +78,7 @@ body: label: Library version description: > Which version of the library did you use? If it is a released version, - please enter the version number (e.g., 3.11.3). Otherwise, please enter + please enter the version number (e.g., 3.12.0). Otherwise, please enter the commit hash. If you got the library from another source as the GitHub repository (e.g., via a package manager), please also state this. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index c9c7cb7931..5370953242 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,19 +1,9 @@ -[Describe your pull request here. Please read the text below the line, and make sure you follow the checklist.] +[Describe your pull request here. Please read the text below the line and make sure you follow the checklist.] -* * * - -## Pull request checklist +- [ ] The changes are described in detail, both the what and why. +- [ ] If applicable, an [existing issue](https://github.com/nlohmann/json/issues) is referenced. +- [ ] The [Code coverage](https://coveralls.io/github/nlohmann/json) remained at 100%. A test case for every new line of code. +- [ ] If applicable, the [documentation](https://json.nlohmann.me) is updated. +- [ ] The source code is amalgamated by running `make amalgamate`. Read the [Contribution Guidelines](https://github.com/nlohmann/json/blob/develop/.github/CONTRIBUTING.md) for detailed information. - -- [ ] Changes are described in the pull request, or an [existing issue is referenced](https://github.com/nlohmann/json/issues). -- [ ] The test suite [compiles and runs](https://github.com/nlohmann/json/blob/develop/README.md#execute-unit-tests) without error. -- [ ] [Code coverage](https://coveralls.io/github/nlohmann/json) is 100%. Test cases can be added by editing the [test suite](https://github.com/nlohmann/json/tree/develop/test/src). -- [ ] The source code is amalgamated; that is, after making changes to the sources in the `include/nlohmann` directory, run `make amalgamate` to create the single-header files `single_include/nlohmann/json.hpp` and `single_include/nlohmann/json_fwd.hpp`. The whole process is described [here](https://github.com/nlohmann/json/blob/develop/.github/CONTRIBUTING.md#files-to-change). - -## Please don't - -- The C++11 support varies between different **compilers** and versions. Please note the [list of supported compilers](https://github.com/nlohmann/json/blob/master/README.md#supported-compilers). Some compilers like GCC 4.7 (and earlier), Clang 3.3 (and earlier), or Microsoft Visual Studio 13.0 and earlier are known not to work due to missing or incomplete C++11 support. Please refrain from proposing changes that work around these compiler's limitations with `#ifdef`s or other means. -- Specifically, I am aware of compilation problems with **Microsoft Visual Studio** (there even is an [issue label](https://github.com/nlohmann/json/issues?utf8=✓&q=label%3A%22visual+studio%22+) for this kind of bug). I understand that even in 2016, complete C++11 support isn't there yet. But please also understand that I do not want to drop features or uglify the code just to make Microsoft's sub-standard compiler happy. The past has shown that there are ways to express the functionality such that the code compiles with the most recent MSVC - unfortunately, this is not the main objective of the project. -- Please refrain from proposing changes that would **break [JSON](https://json.org) conformance**. If you propose a conformant extension of JSON to be supported by the library, please motivate this extension. -- Please do not open pull requests that address **multiple issues**. diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 4d010ebda9..ea7e8810f3 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -2,4 +2,24 @@ ## Reporting a Vulnerability -Usually, all issues are tracked publicly on [GitHub](https://github.com/nlohmann/json/issues). If you want to make a private report (e.g., for a vulnerability or to attach an example that is not meant to be published), please send an email to . You can use [this key](https://keybase.io/nlohmann/pgp_keys.asc?fingerprint=797167ae41c0a6d9232e48457f3cea63ae251b69) for encryption. +We value the security of our users and appreciate your efforts to responsibly disclose vulnerabilities. If you have +identified a security vulnerability in this repository, please use the GitHub Security Advisory +["Report a Vulnerability"](https://github.com/nlohmann/json/security/advisories/new) tab. + +Until it is published, this draft security advisory will only be visible to the maintainers of this project. Other +users and teams may be added once the advisory is created. + +We will send a response indicating the next steps in handling your report. After the initial reply to your report, we +will keep you informed of the progress towards a fix and full announcement and may ask for additional information or +guidance. + +For vulnerabilities in third-party dependencies or modules, please report them directly to the respective maintainers. + +## Additional Resources + +- Explore security-related topics and contribute to tools and projects through + [GitHub Security Lab](https://securitylab.github.com/). +- Learn more about responsible disclosure and reporting vulnerabilities in GitHub at + [About coordinated disclosure of security vulnerabilities](https://docs.github.com/en/code-security/repository-security-advisories/about-coordinated-disclosure-of-security-vulnerabilities). + +We sincerely thank you for contributing to the security and integrity of this project! diff --git a/.github/config.yml b/.github/config.yml index 7a8f41e6d6..4e3217479b 100644 --- a/.github/config.yml +++ b/.github/config.yml @@ -6,7 +6,7 @@ sentimentBotToxicityThreshold: .7 # *Required* Comment to reply with sentimentBotReplyComment: > - Please be sure to review the [code of conduct](https://github.com/nlohmann/json/blob/develop/CODE_OF_CONDUCT.md) and be respectful of other users. cc/ @nlohmann + Please be sure to review the [code of conduct](https://github.com/nlohmann/json/blob/develop/.github/CODE_OF_CONDUCT.md) and be respectful of other users. cc/ @nlohmann # Configuration for request-info - https://github.com/behaviorbot/request-info @@ -17,3 +17,6 @@ requestInfoReplyComment: > # *OPTIONAL* Label to be added to Issues and Pull Requests with insufficient information given requestInfoLabelToAdd: "state: needs more info" + +checkIssueTemplate: true +checkPullRequestTemplate: true diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..d517ed9843 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,31 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: daily + + - package-ecosystem: pip + directory: /docs/mkdocs + schedule: + interval: daily + + - package-ecosystem: pip + directory: /tools/astyle + schedule: + interval: daily + + - package-ecosystem: pip + directory: /tools/generate_natvis + schedule: + interval: daily + + - package-ecosystem: pip + directory: /tools/serve_header + schedule: + interval: daily + + - package-ecosystem: pip + directory: /cmake/requirements + schedule: + interval: daily diff --git a/.github/external_ci/appveyor.yml b/.github/external_ci/appveyor.yml index 126ed99b35..5c6b47a344 100644 --- a/.github/external_ci/appveyor.yml +++ b/.github/external_ci/appveyor.yml @@ -41,7 +41,7 @@ environment: - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 configuration: Release platform: x86 - CXX_FLAGS: "/permissive- /std:c++latest /utf-8 /W4 /WX" + CXX_FLAGS: "/permissive- /std:c++17 /utf-8 /W4 /WX" CMAKE_OPTIONS: "" GENERATOR: Visual Studio 15 2017 @@ -62,7 +62,7 @@ environment: - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 configuration: Release platform: x64 - CXX_FLAGS: "/permissive- /std:c++latest /Zc:__cplusplus /utf-8 /W4 /WX" + CXX_FLAGS: "/permissive- /std:c++17 /Zc:__cplusplus /utf-8 /W4 /WX" CMAKE_OPTIONS: "" GENERATOR: Visual Studio 15 2017 diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index d30c78be77..0000000000 --- a/.github/stale.yml +++ /dev/null @@ -1,17 +0,0 @@ -# Number of days of inactivity before an issue becomes stale -daysUntilStale: 30 -# Number of days of inactivity before a stale issue is closed -daysUntilClose: 7 -# Issues with these labels will never be considered stale -exemptLabels: - - pinned - - security -# Label to use when marking an issue as stale -staleLabel: "state: stale" -# Comment to post when marking an issue as stale. Set to `false` to disable -markComment: > - This issue has been automatically marked as stale because it has not had - recent activity. It will be closed if no further activity occurs. Thank you - for your contributions. -# Comment to post when closing a stale issue. Set to `false` to disable -closeComment: false diff --git a/.github/workflows/check_amalgamation.yml b/.github/workflows/check_amalgamation.yml index 0fadb52065..e7a24f6875 100644 --- a/.github/workflows/check_amalgamation.yml +++ b/.github/workflows/check_amalgamation.yml @@ -3,18 +3,24 @@ name: "Check amalgamation" on: pull_request: -permissions: read-all +permissions: + contents: read jobs: save: runs-on: ubuntu-latest steps: + - name: Harden Runner + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + - name: Save PR number run: | mkdir -p ./pr echo ${{ github.event.number }} > ./pr/number echo ${{ github.event.pull_request.user.login }} > ./pr/author - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: pr path: pr/ @@ -25,29 +31,29 @@ jobs: MAIN_DIR: ${{ github.workspace }}/main INCLUDE_DIR: ${{ github.workspace }}/main/single_include/nlohmann TOOL_DIR: ${{ github.workspace }}/tools/tools/amalgamate - ASTYLE_FLAGS: > - --style=allman --indent=spaces=4 --indent-modifiers --indent-switches --indent-preproc-block - --indent-preproc-define --indent-col1-comments --pad-oper --pad-header --align-pointer=type - --align-reference=type --add-brackets --convert-tabs --close-templates --lineend=linux --preserve-date - --formatted steps: + - name: Harden Runner + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + - name: Checkout pull request - uses: actions/checkout@v3 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: path: main ref: ${{ github.event.pull_request.head.sha }} - name: Checkout tools - uses: actions/checkout@v3 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: path: tools ref: develop - name: Install astyle run: | - sudo apt-get update - sudo apt-get install astyle + python3 -mvenv venv + venv/bin/pip3 install -r $MAIN_DIR/tools/astyle/requirements.txt - name: Check amalgamation run: | @@ -60,11 +66,11 @@ jobs: python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json.json -s . python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json_fwd.json -s . echo "Format (1)" - astyle $ASTYLE_FLAGS --suffix=none --quiet $INCLUDE_DIR/json.hpp $INCLUDE_DIR/json_fwd.hpp + ${{ github.workspace }}/venv/bin/astyle --project=tools/astyle/.astylerc --suffix=none --quiet $INCLUDE_DIR/json.hpp $INCLUDE_DIR/json_fwd.hpp diff $INCLUDE_DIR/json.hpp~ $INCLUDE_DIR/json.hpp diff $INCLUDE_DIR/json_fwd.hpp~ $INCLUDE_DIR/json_fwd.hpp - astyle $ASTYLE_FLAGS $(find docs/examples include tests -type f \( -name '*.hpp' -o -name '*.cpp' -o -name '*.cu' \) -not -path 'tests/thirdparty/*' -not -path 'tests/abi/include/nlohmann/*' | sort) + ${{ github.workspace }}/venv/bin/astyle --project=tools/astyle/.astylerc --suffix=orig $(find docs/examples include tests -type f \( -name '*.hpp' -o -name '*.cpp' -o -name '*.cu' \) -not -path 'tests/thirdparty/*' -not -path 'tests/abi/include/nlohmann/*' | sort) echo Check find $MAIN_DIR -name '*.orig' -exec false {} \+ diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml index 0fd355bce5..5eed4e7056 100644 --- a/.github/workflows/cifuzz.yml +++ b/.github/workflows/cifuzz.yml @@ -6,8 +6,13 @@ permissions: jobs: Fuzzing: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: + - name: Harden Runner + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + - name: Build Fuzzers id: build uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master @@ -23,7 +28,7 @@ jobs: dry-run: false language: c++ - name: Upload Crash - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 if: failure() && steps.build.outcome == 'success' with: name: artifacts diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 93923a1827..244423fb2d 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -11,13 +11,13 @@ on: - cron: '0 19 * * 1' workflow_dispatch: -permissions: - contents: read - concurrency: group: ${{ github.workflow }}-${{ github.ref || github.run_id }} cancel-in-progress: true +permissions: + contents: read + jobs: CodeQL-Build: @@ -26,19 +26,24 @@ jobs: security-events: write steps: + - name: Harden Runner + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9 with: languages: c-cpp # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9 diff --git a/.github/workflows/comment_check_amalgamation.yml b/.github/workflows/comment_check_amalgamation.yml index 2ab5ebb978..4ace5f91bd 100644 --- a/.github/workflows/comment_check_amalgamation.yml +++ b/.github/workflows/comment_check_amalgamation.yml @@ -5,7 +5,8 @@ on: types: - completed -permissions: {} +permissions: + contents: read jobs: comment: @@ -17,8 +18,13 @@ jobs: issues: read pull-requests: write steps: + - name: Harden Runner + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + - name: 'Download artifact' - uses: actions/github-script@98814c53be79b1d30f795b907e553d8679345975 # v6.4.0 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: | var artifacts = await github.rest.actions.listWorkflowRunArtifacts({ @@ -40,7 +46,7 @@ jobs: - run: unzip pr.zip - name: 'Comment on PR' - uses: actions/github-script@98814c53be79b1d30f795b907e553d8679345975 # v6.4.0 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000000..93101e7964 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,27 @@ +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Request, +# surfacing known-vulnerable versions of the packages declared or updated in the PR. +# Once installed, if the workflow run is marked as required, +# PRs introducing known-vulnerable packages will be blocked from merging. +# +# Source repository: https://github.com/actions/dependency-review-action +name: 'Dependency Review' +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: Harden Runner + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + + - name: 'Checkout Repository' + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: 'Dependency Review' + uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2 diff --git a/.github/workflows/flawfinder.yml b/.github/workflows/flawfinder.yml new file mode 100644 index 0000000000..954b296cdb --- /dev/null +++ b/.github/workflows/flawfinder.yml @@ -0,0 +1,46 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: flawfinder + +permissions: + contents: read + +on: + push: + branches: [ "develop" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "develop" ] + schedule: + - cron: '41 14 * * 3' + +jobs: + flawfinder: + name: Flawfinder + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + steps: + - name: Harden Runner + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + + - name: Checkout code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + + - name: flawfinder_scan + uses: david-a-wheeler/flawfinder@c57197cd6061453f10a496f30a732bc1905918d1 # v2.0.19 + with: + arguments: '--sarif ./' + output: 'flawfinder_results.sarif' + + - name: Upload analysis results to GitHub Security tab + uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4 + with: + sarif_file: ${{github.workspace}}/flawfinder_results.sarif diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 11925e1af0..e63f969540 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -4,7 +4,8 @@ on: pull_request_target: types: [opened, synchronize] -permissions: {} +permissions: + contents: read jobs: label: @@ -15,6 +16,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: srvaroa/labeler@master + - name: Harden Runner + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + + - uses: srvaroa/labeler@b4493338d7929ddc4ffc95fadf6f28c73bae2e90 # master env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 9ac1fe3fa7..a24344d479 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -9,24 +9,78 @@ on: pull_request: workflow_dispatch: -permissions: - contents: read - concurrency: group: ${{ github.workflow }}-${{ github.ref || github.run_id }} cancel-in-progress: true +permissions: + contents: read + jobs: - xcode_1: - runs-on: macos-11 +# macos-11 is deprecated +# macos-11: +# runs-on: macos-11 +# strategy: +# matrix: +# xcode: ['11.7', '12.4', '12.5.1', '13.0'] +# env: +# DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer +# +# steps: +# - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 +# - name: Run CMake +# run: cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug -DJSON_BuildTests=On -DJSON_FastTests=ON +# - name: Build +# run: cmake --build build --parallel 10 +# - name: Test +# run: cd build ; ctest -j 10 --output-on-failure + +# macos-12 is deprecated (https://github.com/actions/runner-images/issues/10721) +# macos-12: +# runs-on: macos-12 # https://github.com/actions/runner-images/blob/main/images/macos/macos-12-Readme.md +# strategy: +# matrix: +# xcode: ['13.1', '13.2.1', '13.3.1', '13.4.1', '14.0', '14.0.1', '14.1'] +# env: +# DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer +# +# steps: +# - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 +# - name: Run CMake +# run: cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug -DJSON_BuildTests=On -DJSON_FastTests=ON +# - name: Build +# run: cmake --build build --parallel 10 +# - name: Test +# run: cd build ; ctest -j 10 --output-on-failure + +# macos-13 is deprecated (https://github.com/actions/runner-images/issues/13046) +# macos-13: +# runs-on: macos-13 # https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md +# strategy: +# matrix: +# xcode: ['14.1', '14.2', '14.3', '14.3.1', '15.0.1', '15.1', '15.2'] +# env: +# DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer +# +# steps: +# - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 +# - name: Run CMake +# run: cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug -DJSON_BuildTests=On -DJSON_FastTests=ON +# - name: Build +# run: cmake --build build --parallel 10 +# - name: Test +# run: cd build ; ctest -j 10 --output-on-failure + + macos-14: + runs-on: macos-14 # https://github.com/actions/runner-images/blob/main/images/macos/macos-14-Readme.md strategy: matrix: - xcode: ['11.7', '12.4', '12.5.1', '13.0'] + xcode: ['15.0.1', '15.1', '15.2', '15.3', '15.4'] env: DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Run CMake run: cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug -DJSON_BuildTests=On -DJSON_FastTests=ON - name: Build @@ -34,16 +88,16 @@ jobs: - name: Test run: cd build ; ctest -j 10 --output-on-failure - xcode_2: - runs-on: macos-12 + macos-15: + runs-on: macos-15 # https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md strategy: matrix: - xcode: ['13.1', '13.2.1', '13.3.1', '13.4.1', '14.0', '14.0.1', '14.1'] + xcode: ['16.0', '16.1', '16.2', '16.3', '16.4', '26.0.1'] env: DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Run CMake run: cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug -DJSON_BuildTests=On -DJSON_FastTests=ON - name: Build @@ -55,10 +109,10 @@ jobs: runs-on: macos-latest strategy: matrix: - standard: [11, 14, 17, 20, 23] + standard: [11, 14, 17, 20, 23, 26] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Run CMake run: cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug -DJSON_BuildTests=On -DJSON_TestStandards=${{ matrix.standard }} - name: Build diff --git a/.github/workflows/publish_documentation.yml b/.github/workflows/publish_documentation.yml index 5a32d13a41..fbec85fb0c 100644 --- a/.github/workflows/publish_documentation.yml +++ b/.github/workflows/publish_documentation.yml @@ -10,23 +10,28 @@ on: - docs/examples/** workflow_dispatch: -permissions: - contents: read - # we don't want to have concurrent jobs, and we don't want to cancel running jobs to avoid broken publications concurrency: group: documentation cancel-in-progress: false +permissions: + contents: read + jobs: publish_documentation: + permissions: + contents: write + if: github.repository == 'nlohmann/json' runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - name: Harden Runner + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit - - name: Install and update PlantUML - run: sudo apt-get update ; sudo apt-get install -y plantuml + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Install virtual environment run: make install_venv -C docs/mkdocs @@ -35,7 +40,7 @@ jobs: run: make build -C docs/mkdocs - name: Deploy documentation - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./docs/mkdocs/site diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml new file mode 100644 index 0000000000..0e3b0a6bb9 --- /dev/null +++ b/.github/workflows/scorecards.yml @@ -0,0 +1,81 @@ +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '20 7 * * 2' + push: + branches: ["develop"] + +permissions: + contents: read + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + contents: read + actions: read + # To allow GraphQL ListCommits to work + issues: read + pull-requests: read + # To detect SAST tools + checks: read + + steps: + - name: Harden Runner + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + + - name: "Checkout code" + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecards on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. + # repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9 + with: + sarif_file: results.sarif diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml new file mode 100644 index 0000000000..e4b5f01f5b --- /dev/null +++ b/.github/workflows/semgrep.yml @@ -0,0 +1,54 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +# This workflow file requires a free account on Semgrep.dev to +# manage rules, file ignores, notifications, and more. +# +# See https://semgrep.dev/docs + +name: Semgrep + +on: + push: + branches: [ "develop" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "develop" ] + schedule: + - cron: '23 2 * * 4' + +permissions: + contents: read + +jobs: + semgrep: + permissions: + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + name: Scan + runs-on: ubuntu-latest + steps: + - name: Harden Runner + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + + # Checkout project source + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + + # Scan code using project's configuration on https://semgrep.dev/manage + - uses: returntocorp/semgrep-action@713efdd345f3035192eaa63f56867b88e63e4e5d + with: + publishToken: ${{ secrets.SEMGREP_APP_TOKEN }} + publishDeployment: ${{ secrets.SEMGREP_DEPLOYMENT_ID }} + generateSarif: "1" + + # Upload SARIF file generated in previous step + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4 + with: + sarif_file: semgrep.sarif + if: always() diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000000..b780973791 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,34 @@ +name: 'Comment and close stale issues and PR' +on: + schedule: + - cron: '0 0 * * *' + +permissions: + contents: read + +jobs: + stale: + runs-on: ubuntu-latest + + permissions: + issues: write + pull-requests: write + + steps: + - name: Harden Runner + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + + - uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1 + with: + stale-issue-label: 'state: stale' + stale-pr-label: 'state: stale' + exempt-issue-labels: 'pinned,security' + stale-issue-message: 'This issue has been marked as stale because it has been open for 90 days without activity. If this issue is still relevant, please add a comment or remove the "stale" label. Otherwise, it will be closed in 10 days. Thank you for helping us prioritize our work!' + stale-pr-message: 'This pull request has been marked as stale because it has had no activity for 30 days. While we won’t close it automatically, we encourage you to update or comment if it is still relevant. Keeping pull requests active and up-to-date helps us review and merge changes more efficiently. Thank you for your contributions!' + close-issue-message: 'This issue has been closed after being marked as stale for 10 days without any further activity. If this was done in error or the issue is still relevant, please feel free to reopen it or create a new issue. We appreciate your understanding and contributions.' + days-before-stale: 90 + days-before-pr-stale: 30 + days-before-close: 10 + days-before-pr-close: -1 diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 35fb9573cc..764559571f 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -8,7 +8,7 @@ on: - release/* pull_request: workflow_dispatch: - + permissions: contents: read @@ -17,52 +17,44 @@ concurrency: cancel-in-progress: true jobs: - ci_test_clang: - runs-on: ubuntu-latest - container: silkeh/clang:dev - steps: - - name: Install git and unzip - run: apt-get update ; apt-get install -y git unzip - - uses: actions/checkout@v3 - - name: Get latest CMake and ninja - uses: lukka/get-cmake@v3.27.7 - - name: Run CMake - run: cmake -S . -B build -DJSON_CI=On - - name: Build - run: cmake --build build --target ci_test_clang - ci_test_gcc: runs-on: ubuntu-latest - container: ghcr.io/nlohmann/json-ci:v2.4.0 + container: gcc:latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Get latest CMake and ninja + uses: lukka/get-cmake@9e07ecdcee1b12e5037e42f410b67f03e2f626e1 # v4.2.1 - name: Run CMake run: cmake -S . -B build -DJSON_CI=On - name: Build run: cmake --build build --target ci_test_gcc - ci_static_analysis: + ci_infer: runs-on: ubuntu-latest container: ghcr.io/nlohmann/json-ci:v2.4.0 - strategy: - matrix: - target: [ci_cppcheck, ci_test_valgrind, ci_test_amalgamation, ci_test_single_header, ci_single_binaries, ci_infer] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Run CMake run: cmake -S . -B build -DJSON_CI=On - name: Build - run: cmake --build build --target ${{ matrix.target }} + run: cmake --build build --target ci_infer ci_static_analysis_ubuntu: runs-on: ubuntu-latest strategy: matrix: - target: [ci_cpplint, ci_reproducible_tests, ci_non_git_tests, ci_offline_testdata] + target: [ci_test_amalgamation, ci_test_single_header, ci_cppcheck, ci_cpplint, ci_reproducible_tests, ci_non_git_tests, ci_offline_testdata, ci_reuse_compliance, ci_test_valgrind] steps: - - uses: actions/checkout@v3 + - name: Harden Runner + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + + - name: Install Valgrind + run: sudo apt-get update ; sudo apt-get install -y valgrind + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Get latest CMake and ninja - uses: lukka/get-cmake@v3.27.7 + uses: lukka/get-cmake@9e07ecdcee1b12e5037e42f410b67f03e2f626e1 # v4.2.1 - name: Run CMake run: cmake -S . -B build -DJSON_CI=On - name: Build @@ -73,13 +65,13 @@ jobs: container: silkeh/clang:dev strategy: matrix: - target: [ci_clang_tidy, ci_test_clang_sanitizer, ci_clang_analyze] + target: [ci_test_clang, ci_clang_tidy, ci_test_clang_sanitizer, ci_clang_analyze, ci_single_binaries] steps: - - name: Install git, clang-tools, and unzip - run: apt-get update ; apt-get install -y git clang-tools unzip - - uses: actions/checkout@v3 + - name: Install git, clang-tools, iwyu (ci_single_binaries), and unzip + run: apt-get update ; apt-get install -y git clang-tools iwyu unzip + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Get latest CMake and ninja - uses: lukka/get-cmake@v3.27.7 + uses: lukka/get-cmake@9e07ecdcee1b12e5037e42f410b67f03e2f626e1 # v4.2.1 - name: Run CMake run: cmake -S . -B build -DJSON_CI=On - name: Build @@ -90,13 +82,13 @@ jobs: container: ubuntu:focal strategy: matrix: - target: [ci_cmake_flags, ci_test_diagnostics, ci_test_noexceptions, ci_test_noimplicitconversions, ci_test_legacycomparison, ci_test_noglobaludls] + target: [ci_cmake_flags, ci_test_diagnostics, ci_test_diagnostic_positions, ci_test_noexceptions, ci_test_noimplicitconversions, ci_test_legacycomparison, ci_test_noglobaludls] steps: - name: Install build-essential - run: apt-get update ; apt-get install -y build-essential unzip wget git - - uses: actions/checkout@v3 + run: apt-get update ; apt-get install -y build-essential unzip wget git libssl-dev + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Get latest CMake and ninja - uses: lukka/get-cmake@v3.27.7 + uses: lukka/get-cmake@9e07ecdcee1b12e5037e42f410b67f03e2f626e1 # v4.2.1 - name: Run CMake run: cmake -S . -B build -DJSON_CI=On - name: Build @@ -104,37 +96,60 @@ jobs: ci_test_coverage: runs-on: ubuntu-latest - container: ghcr.io/nlohmann/json-ci:v2.4.0 - permissions: - contents: read - checks: write steps: - - uses: actions/checkout@v3 + - name: Harden Runner + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Install dependencies and de_DE locale + run: | + sudo apt-get clean + sudo apt-get update + sudo apt-get install -y build-essential cmake lcov ninja-build make locales gcc-multilib g++-multilib + sudo locale-gen de_DE + sudo update-locale - name: Run CMake run: cmake -S . -B build -DJSON_CI=On - name: Build run: cmake --build build --target ci_test_coverage - name: Archive coverage report - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: code-coverage-report path: ${{ github.workspace }}/build/html - name: Publish report to Coveralls - uses: coverallsapp/github-action@master + uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2.3.7 with: github-token: ${{ secrets.GITHUB_TOKEN }} path-to-lcov: ${{ github.workspace }}/build/json.info.filtered.noexcept + fail-on-error: false + + ci_test_compilers_gcc_old: + runs-on: ubuntu-latest + strategy: + matrix: + compiler: ['4.8', '4.9', '5', '6'] + container: ghcr.io/nlohmann/json-ci:v2.4.0 + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Run CMake + run: CXX=g++-${{ matrix.compiler }} cmake -S . -B build -DJSON_CI=On + - name: Build + run: cmake --build build --target ci_test_compiler_g++-${{ matrix.compiler }} ci_test_compilers_gcc: runs-on: ubuntu-latest strategy: matrix: - compiler: ['4', '5', '6', '7', '8', '9', '10', '11', '12', 'latest'] + # older GCC docker images (4, 5, 6) fail to check out code + compiler: ['7', '8', '9', '10', '11', '12', '13', '14', '15', 'latest'] container: gcc:${{ matrix.compiler }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Get latest CMake and ninja - uses: lukka/get-cmake@v3.27.7 + uses: lukka/get-cmake@9e07ecdcee1b12e5037e42f410b67f03e2f626e1 # v4.2.1 - name: Run CMake run: cmake -S . -B build -DJSON_CI=On - name: Build @@ -144,14 +159,12 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - compiler: ['3.5', '3.6', '3.7', '3.8', '3.9', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15-bullseye', 'latest'] + compiler: ['3.4', '3.5', '3.6', '3.7', '3.8', '3.9', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15-bullseye', '16', '17', '18', '19', '20', 'latest'] container: silkeh/clang:${{ matrix.compiler }} steps: - - name: Install unzip and git - run: apt-get update ; apt-get install -y unzip git - - uses: actions/checkout@v3 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Get latest CMake and ninja - uses: lukka/get-cmake@v3.27.7 + uses: lukka/get-cmake@9e07ecdcee1b12e5037e42f410b67f03e2f626e1 # v4.2.1 - name: Set env FORCE_STDCPPFS_FLAG for clang 7 / 8 / 9 / 10 run: echo "JSON_FORCED_GLOBAL_COMPILE_OPTIONS=-DJSON_HAS_FILESYSTEM=0;-DJSON_HAS_EXPERIMENTAL_FILESYSTEM=0" >> "$GITHUB_ENV" if: ${{ matrix.compiler == '7' || matrix.compiler == '8' || matrix.compiler == '9' || matrix.compiler == '10' }} @@ -160,27 +173,16 @@ jobs: - name: Build run: cmake --build build --target ci_test_compiler_default - ci_test_compilers: - runs-on: ubuntu-latest - container: ghcr.io/nlohmann/json-ci:v2.4.0 - strategy: - matrix: - compiler: [g++-4.8] - steps: - - uses: actions/checkout@v3 - - name: Run CMake - run: cmake -S . -B build -DJSON_CI=On - - name: Build - run: cmake --build build --target ci_test_compiler_${{ matrix.compiler }} - ci_test_standards_gcc: runs-on: ubuntu-latest - container: ghcr.io/nlohmann/json-ci:v2.4.0 + container: gcc:latest strategy: matrix: - standard: [11, 14, 17, 20, 23] + standard: [11, 14, 17, 20, 23, 26] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Get latest CMake and ninja + uses: lukka/get-cmake@9e07ecdcee1b12e5037e42f410b67f03e2f626e1 # v4.2.1 - name: Run CMake run: cmake -S . -B build -DJSON_CI=On - name: Build @@ -191,33 +193,53 @@ jobs: container: silkeh/clang:latest strategy: matrix: - standard: [11, 14, 17, 20, 23] + standard: [11, 14, 17, 20, 23, 26] + stdlib: [libcxx, libstdcxx] steps: - name: Install git and unzip run: apt-get update ; apt-get install -y git unzip - - uses: actions/checkout@v3 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Get latest CMake and ninja - uses: lukka/get-cmake@v3.27.7 + uses: lukka/get-cmake@9e07ecdcee1b12e5037e42f410b67f03e2f626e1 # v4.2.1 - name: Run CMake run: cmake -S . -B build -DJSON_CI=On - - name: Build + - name: Build with libc++ + run: cmake --build build --target ci_test_clang_libcxx_cxx${{ matrix.standard }} + if: ${{ matrix.stdlib == 'libcxx' }} + - name: Build with libstdc++ run: cmake --build build --target ci_test_clang_cxx${{ matrix.standard }} + if: ${{ matrix.stdlib == 'libstdcxx' }} ci_cuda_example: runs-on: ubuntu-latest container: ghcr.io/nlohmann/json-ci:v2.4.0 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Run CMake run: cmake -S . -B build -DJSON_CI=On - name: Build run: cmake --build build --target ci_cuda_example + ci_module_cpp20: + strategy: + matrix: + container: ['gcc:latest', 'silkeh/clang:latest'] + runs-on: ubuntu-latest + container: ${{ matrix.container }} + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Get latest CMake and ninja + uses: lukka/get-cmake@9e07ecdcee1b12e5037e42f410b67f03e2f626e1 # v4.2.1 + - name: Run CMake + run: cmake -S . -B build -DJSON_CI=On + - name: Build + run: cmake --build build --target ci_module_cpp20 + ci_icpc: runs-on: ubuntu-latest container: ghcr.io/nlohmann/json-ci:v2.2.0 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Run CMake run: cmake -S . -B build -DJSON_CI=On - name: Build @@ -225,23 +247,36 @@ jobs: . /opt/intel/oneapi/setvars.sh cmake --build build --target ci_icpc - ci_reuse_compliance: + ci_emscripten: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 - - name: Install REUSE tool - run: python -m pip install reuse - - name: Run REUSE lint - run: reuse lint + - name: Harden Runner + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + + - name: Install emscripten + uses: mymindstorm/setup-emsdk@6ab9eb1bda2574c4ddb79809fc9247783eaf9021 # v14 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Get latest CMake and ninja + uses: lukka/get-cmake@9e07ecdcee1b12e5037e42f410b67f03e2f626e1 # v4.2.1 + - name: Run CMake + run: cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -GNinja + - name: Build + run: cmake --build build ci_test_documentation: runs-on: ubuntu-latest strategy: matrix: - target: [ci_test_examples, ci_test_api_documentation] + target: [ci_test_examples, ci_test_build_documentation] steps: - - uses: actions/checkout@v3 + - name: Harden Runner + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Run CMake run: cmake -S . -B build -DJSON_CI=On - name: Build diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 7ddd4be256..1b08c888b3 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -18,15 +18,15 @@ concurrency: jobs: mingw: - runs-on: windows-2019 + runs-on: windows-2022 strategy: matrix: architecture: [x64, x86] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Set up MinGW - uses: egor-tensin/setup-mingw@v2 + uses: egor-tensin/setup-mingw@41b837e47d7f85214629d255b9c4bc3fcbe9fd63 # v3.0 with: platform: ${{ matrix.architecture }} version: 12.2.0 # https://github.com/egor-tensin/setup-mingw/issues/14 @@ -37,98 +37,88 @@ jobs: - name: Test run: cd build ; ctest -j 10 -C Debug --output-on-failure - msvc2019: - runs-on: windows-2019 - strategy: - matrix: - build_type: [Debug, Release] - architecture: [Win32, x64] - - steps: - - uses: actions/checkout@v3 - - name: Run CMake - run: cmake -S . -B build -G "Visual Studio 16 2019" -A ${{ matrix.architecture }} -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/W4 /WX" - if: matrix.build_type == 'Release' - - name: Run CMake - run: cmake -S . -B build -G "Visual Studio 16 2019" -A ${{ matrix.architecture }} -DJSON_BuildTests=On -DJSON_FastTests=ON -DCMAKE_CXX_FLAGS="/W4 /WX" - if: matrix.build_type == 'Debug' - - name: Build - run: cmake --build build --config ${{ matrix.build_type }} --parallel 10 - - name: Test - run: cd build ; ctest -j 10 -C ${{ matrix.build_type }} --output-on-failure - - msvc2019_latest: - runs-on: windows-2019 - - steps: - - uses: actions/checkout@v3 - - name: Run CMake - run: cmake -S . -B build -G "Visual Studio 16 2019" -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/permissive- /std:c++latest /utf-8 /W4 /WX" - - name: Build - run: cmake --build build --config Release --parallel 10 - - name: Test - run: cd build ; ctest -j 10 -C Release --output-on-failure - - msvc2022: - runs-on: windows-2022 + msvc: strategy: matrix: build_type: [Debug, Release] architecture: [Win32, x64] + std_version: [default, latest] - steps: - - uses: actions/checkout@v3 - - name: Run CMake - run: cmake -S . -B build -G "Visual Studio 17 2022" -A ${{ matrix.architecture }} -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/W4 /WX" - if: matrix.build_type == 'Release' - - name: Run CMake - run: cmake -S . -B build -G "Visual Studio 17 2022" -A ${{ matrix.architecture }} -DJSON_BuildTests=On -DJSON_FastTests=ON -DCMAKE_CXX_FLAGS="/W4 /WX" - if: matrix.build_type == 'Debug' - - name: Build - run: cmake --build build --config ${{ matrix.build_type }} --parallel 10 - - name: Test - run: cd build ; ctest -j 10 -C ${{ matrix.build_type }} --output-on-failure - - msvc2022_latest: runs-on: windows-2022 steps: - - uses: actions/checkout@v3 - - name: Run CMake - run: cmake -S . -B build -G "Visual Studio 17 2022" -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/permissive- /std:c++latest /utf-8 /W4 /WX" - - name: Build - run: cmake --build build --config Release --parallel 10 - - name: Test - run: cd build ; ctest -j 10 -C Release --output-on-failure + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Set extra CXX_FLAGS for latest std_version + id: cxxflags + run: | + if [ "${{ matrix.std_version }}" = "latest" ]; then + echo "flags=/permissive- /std:c++latest /utf-8 /W4 /WX" >> $GITHUB_ENV + else + echo "flags=/W4 /WX" >> $GITHUB_ENV + fi + shell: bash + - name: Run CMake (Release) + run: cmake -S . -B build -G "Visual Studio 17 2022" -A ${{ matrix.architecture }} -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="$env:flags" + if: matrix.build_type == 'Release' + shell: pwsh + - name: Run CMake (Debug) + run: cmake -S . -B build -G "Visual Studio 17 2022" -A ${{ matrix.architecture }} -DJSON_BuildTests=On -DJSON_FastTests=ON -DCMAKE_CXX_FLAGS="$env:flags" + if: matrix.build_type == 'Debug' + shell: pwsh + - name: Build + run: cmake --build build --config ${{ matrix.build_type }} --parallel 10 + - name: Test + run: cd build ; ctest -j 10 -C ${{ matrix.build_type }} --output-on-failure clang: - runs-on: windows-2019 + runs-on: windows-2022 strategy: matrix: - version: [11, 12, 13, 14, 15] + version: [11.0.1, 12.0.1, 13.0.1, 14.0.6, 15.0.7, 16.0.6, 18.1.8, 19.1.7, 20.1.8] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Install Clang - run: curl -fsSL -o LLVM${{ matrix.version }}.exe https://github.com/llvm/llvm-project/releases/download/llvmorg-${{ matrix.version }}.0.0/LLVM-${{ matrix.version }}.0.0-win64.exe ; 7z x LLVM${{ matrix.version }}.exe -y -o"C:/Program Files/LLVM" + run: curl -fsSL -o LLVM${{ matrix.version }}.exe https://github.com/llvm/llvm-project/releases/download/llvmorg-${{ matrix.version }}/LLVM-${{ matrix.version }}-win64.exe ; 7z x LLVM${{ matrix.version }}.exe -y -o"C:/Program Files/LLVM" + - name: Set up MinGW + uses: egor-tensin/setup-mingw@41b837e47d7f85214629d255b9c4bc3fcbe9fd63 # v3.0 + with: + platform: x64 + version: 12.2.0 # https://github.com/egor-tensin/setup-mingw/issues/14 - name: Run CMake - run: cmake -S . -B build -DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang++.exe" -G"MinGW Makefiles" -DCMAKE_BUILD_TYPE=Debug -DJSON_BuildTests=On + run: cmake -S . -B build ^ + -DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang++.exe" ^ + -DCMAKE_CXX_FLAGS="--target=x86_64-w64-mingw32 -stdlib=libstdc++ -pthread" ^ + -DCMAKE_EXE_LINKER_FLAGS="-lwinpthread" ^ + -G"MinGW Makefiles" ^ + -DCMAKE_BUILD_TYPE=Debug ^ + -DJSON_BuildTests=On - name: Build run: cmake --build build --parallel 10 - name: Test run: cd build ; ctest -j 10 -C Debug --exclude-regex "test-unicode" --output-on-failure - clang-cl-11: - runs-on: windows-2019 + clang-cl-12: + runs-on: windows-2022 strategy: matrix: architecture: [Win32, x64] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Run CMake - run: cmake -S . -B build -G "Visual Studio 16 2019" -A ${{ matrix.architecture }} -T ClangCL -DJSON_BuildTests=On + run: cmake -S . -B build -G "Visual Studio 17 2022" -A ${{ matrix.architecture }} -T ClangCL -DJSON_BuildTests=On - name: Build run: cmake --build build --config Debug --parallel 10 - name: Test run: cd build ; ctest -j 10 -C Debug --exclude-regex "test-unicode" --output-on-failure + + ci_module_cpp20: + runs-on: windows-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Run CMake (Debug) + run: cmake -S . -B build -G "Visual Studio 17 2022" -DJSON_CI=ON -DCMAKE_CXX_FLAGS="/permissive- /std:c++latest /utf-8 /W4 /WX" + - name: Build + run: cmake --build build --config Debug --target ci_module_cpp20 + \ No newline at end of file diff --git a/.gitignore b/.gitignore index bfd52b4c66..03fe8147e6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,9 +4,6 @@ *.gcda .DS_Store -.wsjcpp-logs/* -.wsjcpp/* - /.idea /cmake-build-* @@ -26,12 +23,11 @@ /tests/parse_*_fuzzer # documentation -/docs/docset/docSet.dsidx /docs/docset/JSON_for_Modern_C++.docset/ /docs/docset/JSON_for_Modern_C++.tgz +/docs/docset/docSet.dsidx +/docs/mkdocs/.cache/ /docs/mkdocs/docs/__pycache__/ -/docs/mkdocs/docs/examples/ -/docs/mkdocs/docs/images/json.gif /docs/mkdocs/site/ /docs/mkdocs/venv/ @@ -41,4 +37,11 @@ /serve_header.yml # Swift Package Manager build directory -/.build \ No newline at end of file +/.build + +venv + +nlohmann_json.spdx + +# Bazel-related +MODULE.bazel.lock diff --git a/.lgtm.yml b/.lgtm.yml deleted file mode 100644 index b62f9fb37b..0000000000 --- a/.lgtm.yml +++ /dev/null @@ -1,4 +0,0 @@ -path_classifiers: - thirdparty: - - /tools/amalgamate - - /tools/cpplint diff --git a/.reuse/README.md b/.reuse/README.md deleted file mode 100644 index 29c2b67a1d..0000000000 --- a/.reuse/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# REUSE Software - -This directory contains supporting files to make the project compliant with the REUSE specification. - -The root `Makefile` contains a target `reuse` that updates copyright headers and checks for compliance. - -See for more information. diff --git a/.reuse/dep5 b/.reuse/dep5 index 315cae923b..2e78ec3af6 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -4,11 +4,15 @@ Upstream-Contact: Niels Lohmann Source: https://github.com/nlohmann/json Files: * -Copyright: 2013-2022 Niels Lohmann +Copyright: 2013-2026 Niels Lohmann License: MIT +Files: include/nlohmann/thirdparty/hedley.hpp +Copyright: 2016-2021 Evan Nemerson +License: CC0 + Files: tests/thirdparty/doctest/* -Copyright: 2016-2021 Viktor Kirilov +Copyright: 2016-2023 Viktor Kirilov License: MIT Files: tests/thirdparty/fifo_map/* @@ -16,7 +20,7 @@ Copyright: 2015-2017 Niels Lohmann License: MIT Files: tests/thirdparty/Fuzzer/* -Copyright: 2003-2022, LLVM Project. +Copyright: 2003-2022 LLVM Project. License: Apache-2.0 Files: tests/thirdparty/imapdl/* @@ -27,6 +31,6 @@ Files: tools/amalgamate/* Copyright: 2012 Erik Edlund License: BSD-3-Clause -Files: tools/gdb_pretty_printer +Files: tools/gdb_pretty_printer/* Copyright: 2020 Hannes Domani License: MIT diff --git a/.reuse/templates/json.jinja2 b/.reuse/templates/json.jinja2 index 9f7df2a396..095e3def91 100644 --- a/.reuse/templates/json.jinja2 +++ b/.reuse/templates/json.jinja2 @@ -1,6 +1,6 @@ __ _____ _____ _____ __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 3.11.3 +| | |__ | | | | | | version 3.12.0 |_____|_____|_____|_|___| https://github.com/nlohmann/json {% for copyright_line in copyright_lines %} diff --git a/.reuse/templates/json_support.jinja2 b/.reuse/templates/json_support.jinja2 index f12832cb8e..e239bb7665 100644 --- a/.reuse/templates/json_support.jinja2 +++ b/.reuse/templates/json_support.jinja2 @@ -1,6 +1,6 @@ __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (supporting code) -| | |__ | | | | | | version 3.11.3 +| | |__ | | | | | | version 3.12.0 |_____|_____|_____|_|___| https://github.com/nlohmann/json {% for copyright_line in copyright_lines %} diff --git a/BUILD.bazel b/BUILD.bazel index 15d84f16be..de0ff7145d 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1,3 +1,20 @@ +load("@rules_cc//cc:cc_library.bzl", "cc_library") +load("@rules_license//rules:license.bzl", "license") + +package( + default_applicable_licenses = [":license"], +) + +exports_files([ + "LICENSE.MIT", +]) + +license( + name = "license", + license_kinds = ["@rules_license//licenses/spdx:MIT"], + license_text = "LICENSE.MIT", +) + cc_library( name = "json", hdrs = [ @@ -40,6 +57,7 @@ cc_library( "include/nlohmann/detail/output/serializer.hpp", "include/nlohmann/detail/string_concat.hpp", "include/nlohmann/detail/string_escape.hpp", + "include/nlohmann/detail/string_utils.hpp", "include/nlohmann/detail/value_t.hpp", "include/nlohmann/json.hpp", "include/nlohmann/json_fwd.hpp", @@ -51,3 +69,12 @@ cc_library( visibility = ["//visibility:public"], alwayslink = True, ) + +cc_library( + name = "singleheader-json", + hdrs = [ + "single_include/nlohmann/json.hpp", + ], + includes = ["single_include"], + visibility = ["//visibility:public"], +) diff --git a/CITATION.cff b/CITATION.cff index fd3b767135..8a70c17bc2 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -7,8 +7,8 @@ authors: email: mail@nlohmann.me website: https://nlohmann.me title: "JSON for Modern C++" -version: 3.11.3 -date-released: 2023-11-28 +version: 3.12.0 +date-released: 2025-04-07 license: MIT repository-code: "https://github.com/nlohmann" url: https://json.nlohmann.me diff --git a/CMakeLists.txt b/CMakeLists.txt index 7a49dc47ea..6415b38387 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,10 @@ -cmake_minimum_required(VERSION 3.1...3.14) +cmake_minimum_required(VERSION 3.5...4.0) ## ## PROJECT ## name and version ## -project(nlohmann_json VERSION 3.11.3 LANGUAGES CXX) +project(nlohmann_json VERSION 3.12.0 LANGUAGES CXX) ## ## MAIN_PROJECT CHECK @@ -22,6 +22,18 @@ endif() set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) include(ExternalProject) +# ---- C++ Modules Support (optional) ---- +option(NLOHMANN_JSON_BUILD_MODULES "Build C++ modules support" OFF) + +if(NLOHMANN_JSON_BUILD_MODULES) + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.28) + message(STATUS "Building nlohmann.json C++ module") + add_subdirectory(src/modules) + else() + message(WARNING "Skipping nlohmann.json C++ module (requires CMake 3.28+, found ${CMAKE_VERSION})") + endif() +endif() + ## ## OPTIONS ## @@ -40,7 +52,8 @@ endif() option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enabled." ${JSON_BuildTests_INIT}) option(JSON_CI "Enable CI build targets." OFF) option(JSON_Diagnostics "Use extended diagnostic messages." OFF) -option(JSON_GlobalUDLs "Place use-defined string literals in the global namespace." ON) +option(JSON_Diagnostic_Positions "Enable diagnostic positions." OFF) +option(JSON_GlobalUDLs "Place user-defined string literals in the global namespace." ON) option(JSON_ImplicitConversions "Enable implicit conversions." ON) option(JSON_DisableEnumSerialization "Disable default integer enum serialization." OFF) option(JSON_LegacyDiscardedValueComparison "Enable legacy discarded value comparison." OFF) @@ -57,7 +70,11 @@ endif () ## include(GNUInstallDirs) -set(NLOHMANN_JSON_TARGET_NAME ${PROJECT_NAME}) +if (NOT DEFINED NLOHMANN_JSON_TARGET_NAME) + # Allow overriding the target name when using FetchContent / add_subdirectory. + set(NLOHMANN_JSON_TARGET_NAME ${PROJECT_NAME}) +endif() + set(NLOHMANN_JSON_CONFIG_INSTALL_DIR "${CMAKE_INSTALL_DATADIR}/cmake/${PROJECT_NAME}" CACHE INTERNAL "") set(NLOHMANN_JSON_INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}") set(NLOHMANN_JSON_TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") @@ -77,19 +94,27 @@ else() endif() if (NOT JSON_ImplicitConversions) - message(STATUS "Implicit conversions are disabled") + message(STATUS "Implicit conversions are disabled (JSON_USE_IMPLICIT_CONVERSIONS=0)") endif() if (JSON_DisableEnumSerialization) - message(STATUS "Enum integer serialization is disabled") + message(STATUS "Enum integer serialization is disabled (JSON_DISABLE_ENUM_SERIALIZATION=0)") endif() if (JSON_LegacyDiscardedValueComparison) - message(STATUS "Legacy discarded value comparison enabled") + message(STATUS "Legacy discarded value comparison enabled (JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1)") endif() if (JSON_Diagnostics) - message(STATUS "Diagnostics enabled") + message(STATUS "Diagnostics enabled (JSON_DIAGNOSTICS=1)") +endif() + +if (JSON_Diagnostic_Positions) + message(STATUS "Diagnostic positions enabled (JSON_DIAGNOSTIC_POSITIONS=1)") +endif() + +if (NOT JSON_GlobalUDLs) + message(STATUS "User-defined string literals are not put in the global namespace (JSON_USE_GLOBAL_UDLS=0)") endif() if (JSON_SystemInclude) @@ -115,6 +140,7 @@ target_compile_definitions( $<$>:JSON_USE_IMPLICIT_CONVERSIONS=0> $<$:JSON_DISABLE_ENUM_SERIALIZATION=1> $<$:JSON_DIAGNOSTICS=1> + $<$:JSON_DIAGNOSTIC_POSITIONS=1> $<$:JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1> ) @@ -141,6 +167,7 @@ endif() CONFIGURE_FILE( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/pkg-config.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc" + @ONLY ) ## @@ -206,4 +233,5 @@ if(JSON_Install) FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc" DESTINATION ${NLOHMANN_JSON_PKGCONFIG_INSTALL_DIR} ) + include(CPack) endif() diff --git a/ChangeLog.md b/ChangeLog.md index 656d68bcfc..2274fb455f 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,9 +1,340 @@ # Changelog All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## [3.11.2](https://github.com/nlohmann/json/releases/tag/3.11.2) (2022-08-12) +## [unreleased](https://github.com/nlohmann/json/releases/tag/unreleased) (2024-12-22) + +[Full Changelog](https://github.com/nlohmann/json/compare/v3.11.3...unreleased) + +- Impossible de read json file create with nlohmann::ordered\_json::dump [\#4556](https://github.com/nlohmann/json/issues/4556) +- Error C2039 : 'json\_sax\_dom\_callback\_parser': is not a member of 'nlohmann::json\_abi\_v3\_11\_3::detail' [\#4529](https://github.com/nlohmann/json/issues/4529) +- `json_fwd.hpp` don't define default template arguments for ordered\_map [\#4518](https://github.com/nlohmann/json/issues/4518) +- new repo version seems stop create any the ingress-nginx controller with opentelemetry-cpp.git [\#4515](https://github.com/nlohmann/json/issues/4515) +- Error converting to/from scoped enumerations [\#4499](https://github.com/nlohmann/json/issues/4499) +- Default initialized iterators are not comparable [\#4493](https://github.com/nlohmann/json/issues/4493) +- Bug json.exception.type\_error.302 [\#4492](https://github.com/nlohmann/json/issues/4492) +- tests fail to build with clang-19 and libc++ due to unsupported `std::char_traits` [\#4490](https://github.com/nlohmann/json/issues/4490) +- Brace-Initialization Fails with json::parse and Key Access on Linux [\#4488](https://github.com/nlohmann/json/issues/4488) +- Crash when parsing nullptr [\#4485](https://github.com/nlohmann/json/issues/4485) +- Namespace macros are not respected in many instances [\#4484](https://github.com/nlohmann/json/issues/4484) +- ohos model to json string garbage characters [\#4481](https://github.com/nlohmann/json/issues/4481) +- Missing newlines in deserialized string [\#4479](https://github.com/nlohmann/json/issues/4479) +- Latest tag not available on NuGet [\#4478](https://github.com/nlohmann/json/issues/4478) +- Invalid union access for get\_ref/get\_ptr with unsigned integer [\#4475](https://github.com/nlohmann/json/issues/4475) +- /accesswallet [\#4469](https://github.com/nlohmann/json/issues/4469) +- struct reflect json with error C2440 [\#4467](https://github.com/nlohmann/json/issues/4467) +- Compiler error when using macro NLOHMANN\_DEFINE\_TYPE\_NON\_INTRUSIVE [\#4463](https://github.com/nlohmann/json/issues/4463) +- Issue when dumping a vector of derived classes [\#4462](https://github.com/nlohmann/json/issues/4462) +- whit std::wstring compile error [\#4460](https://github.com/nlohmann/json/issues/4460) +- Inconsisten operator\[\] [\#4458](https://github.com/nlohmann/json/issues/4458) +- json parse enclosing json object with \[\] [\#4457](https://github.com/nlohmann/json/issues/4457) +- \[bug\] nlohmann::json constructor behaves improperly [\#4450](https://github.com/nlohmann/json/issues/4450) +- parse OOM [\#4449](https://github.com/nlohmann/json/issues/4449) +- Library Cannot Parse JSON File It Wrote [\#4448](https://github.com/nlohmann/json/issues/4448) +- Unexpected Integer Conversion of JSON Values on ARM64 [\#4447](https://github.com/nlohmann/json/issues/4447) +- Structure declared in natvis file template doesn't seem to match current structure of `basic_json<>` [\#4438](https://github.com/nlohmann/json/issues/4438) +- A lot of EOT in json file [\#4436](https://github.com/nlohmann/json/issues/4436) +- CVE-2024-34062 [\#4429](https://github.com/nlohmann/json/issues/4429) +- CVE-2024-39689 [\#4428](https://github.com/nlohmann/json/issues/4428) +- CVE-2024-5569 [\#4427](https://github.com/nlohmann/json/issues/4427) +- CVE-2024-37891 [\#4426](https://github.com/nlohmann/json/issues/4426) +- Tornado vulnerabilities [\#4425](https://github.com/nlohmann/json/issues/4425) +- CVE-2024-35195 [\#4424](https://github.com/nlohmann/json/issues/4424) +- CVE-2024-22195, CVE-2024-34064 [\#4423](https://github.com/nlohmann/json/issues/4423) +- CVE-2024-3651 [\#4422](https://github.com/nlohmann/json/issues/4422) +- CVE-2024-22190 [\#4421](https://github.com/nlohmann/json/issues/4421) +- CVE-2024-39705 [\#4420](https://github.com/nlohmann/json/issues/4420) +- Failing to read complex Unicode string embedded in JSON [\#4417](https://github.com/nlohmann/json/issues/4417) +- Unable to parse JSON string from snake case to camel case [\#4399](https://github.com/nlohmann/json/issues/4399) +- Crashes when I try to use ‘json::at\(\)’ on a properly structured, non null, and correctly constructed ‘.json’ file [\#4387](https://github.com/nlohmann/json/issues/4387) +- JSON\_BuildTests fail when JSON\_DisableEnumSerialization is set to ON [\#4384](https://github.com/nlohmann/json/issues/4384) +- JSON can't parse a simple data [\#4383](https://github.com/nlohmann/json/issues/4383) +- json.exception.type\_error.302 [\#4373](https://github.com/nlohmann/json/issues/4373) +- iteration\_proxy has limited usefulness in C++20 range views [\#4371](https://github.com/nlohmann/json/issues/4371) +- Clone is long due to large history [\#4370](https://github.com/nlohmann/json/issues/4370) +- Can't use nlohmann on Linux [\#4363](https://github.com/nlohmann/json/issues/4363) +- CodeQL suppressions lack justification [\#4361](https://github.com/nlohmann/json/issues/4361) +- \[json.exception.parse\_error.101\] parse error at line 1, column 4520: syntax error while parsing value - invalid string: forbidden character after backslash; last read: '".\? [\#4352](https://github.com/nlohmann/json/issues/4352) +- Cannot unflatten json object [\#4349](https://github.com/nlohmann/json/issues/4349) +- Json parsed from raw string does not interpret lists of objects like json parsed from file [\#4341](https://github.com/nlohmann/json/issues/4341) +- natvis not updated to 3.11.3 [\#4340](https://github.com/nlohmann/json/issues/4340) +- Wrong function name in documentation example [\#4334](https://github.com/nlohmann/json/issues/4334) +- git ref got interpreted as number [\#4332](https://github.com/nlohmann/json/issues/4332) +- Is float valid number been limited? [\#4322](https://github.com/nlohmann/json/issues/4322) +- Crash when construct a new json object [\#4321](https://github.com/nlohmann/json/issues/4321) +- gdb-pretty-print broken since m\_data added [\#4309](https://github.com/nlohmann/json/issues/4309) +- Docs have incorrect info for `update()` [\#4307](https://github.com/nlohmann/json/issues/4307) +- CBOR data cannot be decoded [\#4301](https://github.com/nlohmann/json/issues/4301) +- Inconsistent behaviour of json construction using `std::initializer_list` [\#4299](https://github.com/nlohmann/json/issues/4299) +- Assertion failed when accessing non-existing object with const json object [\#4297](https://github.com/nlohmann/json/issues/4297) +- Validatable release artifacts are not sufficient for packaging \(trying to run tests\) [\#4296](https://github.com/nlohmann/json/issues/4296) +- ordered json pointer corruption [\#4289](https://github.com/nlohmann/json/issues/4289) +- Incorrect floating point parsing [\#4285](https://github.com/nlohmann/json/issues/4285) +- Segfault on parse when using "\#pragma pack \(push, 1\)" [\#4284](https://github.com/nlohmann/json/issues/4284) +- Simple example with nlohmann::ordered\_json doesn't compile [\#4282](https://github.com/nlohmann/json/issues/4282) +- Program crashes with ordered\_json, but works fine with json [\#4279](https://github.com/nlohmann/json/issues/4279) +- JSON parses as array when assigned in initializer list. [\#4278](https://github.com/nlohmann/json/issues/4278) +- Can't run `make amalgamate` [\#4275](https://github.com/nlohmann/json/issues/4275) +- Parsing the unicode string got the wrong result [\#4272](https://github.com/nlohmann/json/issues/4272) +- Issue with including \ [\#4241](https://github.com/nlohmann/json/issues/4241) +- Exception when trying to insert my json object inside json file [\#4239](https://github.com/nlohmann/json/issues/4239) +- `to_json` is erroneously converting enums with underlying unsigned types to signed numbers [\#4236](https://github.com/nlohmann/json/issues/4236) +- Build failure on macOS Sonoma 14.1.1 [\#4228](https://github.com/nlohmann/json/issues/4228) +- Deprecation warning on save action in check-amalgamation CI step [\#4227](https://github.com/nlohmann/json/issues/4227) +- 3.11.3: test suite fails in 4 units [\#4224](https://github.com/nlohmann/json/issues/4224) +- Exception thrown when dumping utf-8 characters when using std::string [\#4213](https://github.com/nlohmann/json/issues/4213) +- patch\_inplace assumes StringType is std::string [\#4134](https://github.com/nlohmann/json/issues/4134) +- Getting a weak-vtables warning with clang on ubuntu 22.04 [\#4087](https://github.com/nlohmann/json/issues/4087) +- SAX interface unexpectedly gets locale-altered float representation. [\#4084](https://github.com/nlohmann/json/issues/4084) +- Feat: hash pin github workflow dependencies [\#4058](https://github.com/nlohmann/json/issues/4058) +- compiler error using clang-16.0.5 when using gcc-13.1 standard library [\#4051](https://github.com/nlohmann/json/issues/4051) +- Missing requirement on `plantuml` binary package [\#4026](https://github.com/nlohmann/json/issues/4026) +- Compile failure for macos 10.10 SDK + darwin14 [\#3991](https://github.com/nlohmann/json/issues/3991) +- Test suite does not compile with C++20 and Clang 17 [\#3979](https://github.com/nlohmann/json/issues/3979) +- `modernize-avoid-c-arrays` clang-tidy warning when using `NLOHMANN_JSON_SERIALIZE_ENUM` macro [\#3924](https://github.com/nlohmann/json/issues/3924) +- JSON\_DIAGNOSTICS trigger assertion [\#3915](https://github.com/nlohmann/json/issues/3915) +- Compiler warning 'array-bounds' on g++12.2.0 on Ubuntu 22.10 kinetic with RelWithDebugInfo [\#3808](https://github.com/nlohmann/json/issues/3808) +- The MSVC team recently test JSON project failed to run test on release configuration on windows\_x64. [\#3542](https://github.com/nlohmann/json/issues/3542) +- Bad JSON diff when removing object in array of object [\#3146](https://github.com/nlohmann/json/issues/3146) +- Limit AppVeyor use [\#3089](https://github.com/nlohmann/json/issues/3089) +- pkgconfig integration wrongly rendered if tests are run [\#2907](https://github.com/nlohmann/json/issues/2907) +- Compile error for json in template and variadic macros. [\#2794](https://github.com/nlohmann/json/issues/2794) +- How to Serialize derived class to JSON object? [\#2199](https://github.com/nlohmann/json/issues/2199) +- \[C++17\] Allow std::optional to convert to nlohmann::json [\#1749](https://github.com/nlohmann/json/issues/1749) -[Full Changelog](https://github.com/nlohmann/json/compare/v3.11.1...3.11.2) +- Fix typo in nlohmann\_define\_derived\_type.md [\#4565](https://github.com/nlohmann/json/pull/4565) ([gregmarr](https://github.com/gregmarr)) +- Add ONLY\_SERIALIZE for NLOHMANN\_DEFINE\_DERIVED\_TYPE\_\* macros [\#4562](https://github.com/nlohmann/json/pull/4562) ([EinarsNG](https://github.com/EinarsNG)) +- Suppress modernize-use-integer-sign-comparison [\#4558](https://github.com/nlohmann/json/pull/4558) ([nlohmann](https://github.com/nlohmann)) +- Bump actions/upload-artifact from 4.4.3 to 4.5.0 [\#4557](https://github.com/nlohmann/json/pull/4557) ([dependabot[bot]](https://github.com/apps/dependabot)) +- Clean up CI [\#4553](https://github.com/nlohmann/json/pull/4553) ([nlohmann](https://github.com/nlohmann)) +- \[StepSecurity\] ci: Harden GitHub Actions [\#4551](https://github.com/nlohmann/json/pull/4551) ([step-security-bot](https://github.com/step-security-bot)) +- Fix token permissions warnings [\#4550](https://github.com/nlohmann/json/pull/4550) ([nlohmann](https://github.com/nlohmann)) +- Add step to build the documentation [\#4549](https://github.com/nlohmann/json/pull/4549) ([nlohmann](https://github.com/nlohmann)) +- Bump mkdocs-material from 9.5.48 to 9.5.49 in /docs/mkdocs [\#4548](https://github.com/nlohmann/json/pull/4548) ([dependabot[bot]](https://github.com/apps/dependabot)) +- Move reuse dependency to requirements.txt file [\#4547](https://github.com/nlohmann/json/pull/4547) ([nlohmann](https://github.com/nlohmann)) +- Clean up [\#4546](https://github.com/nlohmann/json/pull/4546) ([nlohmann](https://github.com/nlohmann)) +- ⬆️ Bump ossf/scorecard-action from 2.3.3 to 2.4.0 [\#4545](https://github.com/nlohmann/json/pull/4545) ([dependabot[bot]](https://github.com/apps/dependabot)) +- ⬆️ Bump lukka/get-cmake from 3.31.0 to 3.31.2 [\#4544](https://github.com/nlohmann/json/pull/4544) ([dependabot[bot]](https://github.com/apps/dependabot)) +- ⬆️ Bump github/codeql-action from 2.27.9 to 3.27.9 [\#4543](https://github.com/nlohmann/json/pull/4543) ([dependabot[bot]](https://github.com/apps/dependabot)) +- ⬆️ Bump watchdog from 2.1.7 to 6.0.0 in /tools/serve\_header [\#4542](https://github.com/nlohmann/json/pull/4542) ([dependabot[bot]](https://github.com/apps/dependabot)) +- ⬆️ Bump pyyaml from 6.0 to 6.0.2 in /tools/serve\_header [\#4541](https://github.com/nlohmann/json/pull/4541) ([dependabot[bot]](https://github.com/apps/dependabot)) +- ⬆️ Bump actions/github-script from 6.4.0 to 7.0.1 [\#4540](https://github.com/nlohmann/json/pull/4540) ([dependabot[bot]](https://github.com/apps/dependabot)) +- \[StepSecurity\] Apply security best practices [\#4539](https://github.com/nlohmann/json/pull/4539) ([step-security-bot](https://github.com/step-security-bot)) +- Set parents after insert call [\#4537](https://github.com/nlohmann/json/pull/4537) ([nlohmann](https://github.com/nlohmann)) +- Allow patch and diff to be used with arbitrary string types [\#4536](https://github.com/nlohmann/json/pull/4536) ([nlohmann](https://github.com/nlohmann)) +- Add more package managers [\#4533](https://github.com/nlohmann/json/pull/4533) ([nlohmann](https://github.com/nlohmann)) +- Replace EOF with char\_traits [\#4532](https://github.com/nlohmann/json/pull/4532) ([nlohmann](https://github.com/nlohmann)) +- Fix return value of get\_ptr for unsigned integers [\#4525](https://github.com/nlohmann/json/pull/4525) ([nlohmann](https://github.com/nlohmann)) +- Add more GCC warning flags [\#4524](https://github.com/nlohmann/json/pull/4524) ([nlohmann](https://github.com/nlohmann)) +- Update licenses [\#4521](https://github.com/nlohmann/json/pull/4521) ([nlohmann](https://github.com/nlohmann)) +- json start/end position implementation [\#4517](https://github.com/nlohmann/json/pull/4517) ([sushshring](https://github.com/sushshring)) +- Overwork documentation [\#4516](https://github.com/nlohmann/json/pull/4516) ([nlohmann](https://github.com/nlohmann)) +- Allow comparing default initialized iterators [\#4512](https://github.com/nlohmann/json/pull/4512) ([nlohmann](https://github.com/nlohmann)) +- fix: integer parsed as float when EINTR set in errno [\#4506](https://github.com/nlohmann/json/pull/4506) ([StuartGorman](https://github.com/StuartGorman)) +- Make SAX output locale-independent [\#4505](https://github.com/nlohmann/json/pull/4505) ([nlohmann](https://github.com/nlohmann)) +- Skip enum tests when JSON\_DisableEnumSerialization=ON [\#4504](https://github.com/nlohmann/json/pull/4504) ([nlohmann](https://github.com/nlohmann)) +- Fix weak-vtables warning [\#4500](https://github.com/nlohmann/json/pull/4500) ([nlohmann](https://github.com/nlohmann)) +- Suppress warnings in NLOHMANN\_JSON\_SERIALIZE\_ENUM [\#4497](https://github.com/nlohmann/json/pull/4497) ([nlohmann](https://github.com/nlohmann)) +- Add comment for \#4494 [\#4496](https://github.com/nlohmann/json/pull/4496) ([nlohmann](https://github.com/nlohmann)) +- Add test for libstdc++ [\#4495](https://github.com/nlohmann/json/pull/4495) ([nlohmann](https://github.com/nlohmann)) +- Another desperate try to fix the CI [\#4489](https://github.com/nlohmann/json/pull/4489) ([nlohmann](https://github.com/nlohmann)) +- Possible fix for \#4485 [\#4487](https://github.com/nlohmann/json/pull/4487) ([jordan-hoang](https://github.com/jordan-hoang)) +- Update CONTRIBUTING.md [\#4486](https://github.com/nlohmann/json/pull/4486) ([zerocukor287](https://github.com/zerocukor287)) +- Allow overriding the CMake target name [\#4483](https://github.com/nlohmann/json/pull/4483) ([iboB](https://github.com/iboB)) +- Update is\_structured.md [\#4472](https://github.com/nlohmann/json/pull/4472) ([thetimr](https://github.com/thetimr)) +- Add CPack support [\#4459](https://github.com/nlohmann/json/pull/4459) ([zjyhjqs](https://github.com/zjyhjqs)) +- CMake: generate a pkg-config file that follow pkg-config conventions [\#4456](https://github.com/nlohmann/json/pull/4456) ([dcbaker](https://github.com/dcbaker)) +- Update natvis to reflect 3.11.3 and the current structure of basic\_json [\#4451](https://github.com/nlohmann/json/pull/4451) ([gmyers18](https://github.com/gmyers18)) +- Docs: fix typos of 'whether' in `operator_{gt,le,lt}.md` [\#4412](https://github.com/nlohmann/json/pull/4412) ([tsnl](https://github.com/tsnl)) +- Remove alwayslink=True Bazel flag [\#4396](https://github.com/nlohmann/json/pull/4396) ([mering](https://github.com/mering)) +- Optimize binary `get_number` implementation by reading multiple bytes at once [\#4391](https://github.com/nlohmann/json/pull/4391) ([TianyiChen](https://github.com/TianyiChen)) +- Make iterator\_proxy\_value a forward\_iterator \(\#4371\) [\#4372](https://github.com/nlohmann/json/pull/4372) ([captaincrutches](https://github.com/captaincrutches)) +- Add lgtm explanation [\#4362](https://github.com/nlohmann/json/pull/4362) ([nlohmann](https://github.com/nlohmann)) +- chore: fix some typos in comments [\#4345](https://github.com/nlohmann/json/pull/4345) ([laterlaugh](https://github.com/laterlaugh)) +- Fix gdb pretty printer [\#4343](https://github.com/nlohmann/json/pull/4343) ([MrJia1997](https://github.com/MrJia1997)) +- Fix for incorrect function name in documentation example [\#4342](https://github.com/nlohmann/json/pull/4342) ([alexprabhat99](https://github.com/alexprabhat99)) +- Fixed an error in the `Custom data source` example. [\#4335](https://github.com/nlohmann/json/pull/4335) ([philip-paul-mueller](https://github.com/philip-paul-mueller)) +- Updated exception handling to catch const reference in out\_of\_range [\#4331](https://github.com/nlohmann/json/pull/4331) ([LeilaShcheglova](https://github.com/LeilaShcheglova)) +- \#4307 Updated docx to 3.10.5 from 3.10.4 [\#4310](https://github.com/nlohmann/json/pull/4310) ([AniketDhemare](https://github.com/AniketDhemare)) +- Align astyle flags in Makefile with CI [\#4277](https://github.com/nlohmann/json/pull/4277) ([serge-s](https://github.com/serge-s)) +- Suppress Clang-Tidy warnings [\#4276](https://github.com/nlohmann/json/pull/4276) ([nlohmann](https://github.com/nlohmann)) +- Remove broken link from CONTRIBUTING.md [\#4274](https://github.com/nlohmann/json/pull/4274) ([serge-s](https://github.com/serge-s)) +- Fix version in json\_has\_static\_rtti.md [\#4269](https://github.com/nlohmann/json/pull/4269) ([ALF-ONE](https://github.com/ALF-ONE)) +- Add support of multi-dim C-style array member of struct. [\#4262](https://github.com/nlohmann/json/pull/4262) ([peng-wang-cn](https://github.com/peng-wang-cn)) +- Docs: Fix wrong code usage in the Value access section of `json_pointer.md` [\#4255](https://github.com/nlohmann/json/pull/4255) ([Fallen-Breath](https://github.com/Fallen-Breath)) +- Fix `to_json` for enums when the enum has an unsigned underlying type. [\#4237](https://github.com/nlohmann/json/pull/4237) ([TheJCAB](https://github.com/TheJCAB)) +- feat: Rebase `feature/optional` to `develop` [\#4036](https://github.com/nlohmann/json/pull/4036) ([fsandhei](https://github.com/fsandhei)) +- Add NLOHMANN\_DEFINE\_DERIVED\_TYPE\_\* macros [\#4033](https://github.com/nlohmann/json/pull/4033) ([rotolof](https://github.com/rotolof)) + +## [v3.11.3](https://github.com/nlohmann/json/releases/tag/v3.11.3) (2023-11-28) + +[Full Changelog](https://github.com/nlohmann/json/compare/v3.11.2...v3.11.3) + +- Parser and constructor resolve integer types differently [\#4207](https://github.com/nlohmann/json/issues/4207) +- README.md overuses `template` keyword before `get` function [\#4205](https://github.com/nlohmann/json/issues/4205) +- Exception SIGSEGV - Segmentation violation signal on file parsing \(v3.11.2, linux, doctest\) [\#4193](https://github.com/nlohmann/json/issues/4193) +- In highly nested functions, passing json into a function leads to a segmentation fault/bus error [\#4186](https://github.com/nlohmann/json/issues/4186) +- why a single-object json file appears in an array [\#4183](https://github.com/nlohmann/json/issues/4183) +- Initializing `json` by direct initialization and copy initialization invokes different constructors [\#4174](https://github.com/nlohmann/json/issues/4174) +- Deprecation warning about std::char\_traits\ [\#4163](https://github.com/nlohmann/json/issues/4163) +- LLVM 16.0.6 issues warning for literal operators when Wdeprecated-literal-operator [\#4129](https://github.com/nlohmann/json/issues/4129) +- GCC compiler warning about violating the C++ One Definition Rule \[-Wodr\] [\#4116](https://github.com/nlohmann/json/issues/4116) +- error: building nlohmann-json:arm64-osx failed with: BUILD\_FAILED [\#4091](https://github.com/nlohmann/json/issues/4091) +- dump\(\): Non-conforming with JSON-spec escape of strings? [\#4088](https://github.com/nlohmann/json/issues/4088) +- Compiling in visual studio 2022 gives a warning [\#4081](https://github.com/nlohmann/json/issues/4081) +- Upgrade CMake minimum version [\#4076](https://github.com/nlohmann/json/issues/4076) +- \ character in the content of a string cause error in parser.? [\#4067](https://github.com/nlohmann/json/issues/4067) +- JSON Parsing Freeze Issue on Nintendo Switch [\#4066](https://github.com/nlohmann/json/issues/4066) +- Clang++ compilation fails on extremely small example [\#4061](https://github.com/nlohmann/json/issues/4061) +- how about open a new repository for header only version? [\#4060](https://github.com/nlohmann/json/issues/4060) +- json::count returns only 0 or 1 [\#4052](https://github.com/nlohmann/json/issues/4052) +- std::function error [\#4050](https://github.com/nlohmann/json/issues/4050) +- Json package not compiling properly [\#4042](https://github.com/nlohmann/json/issues/4042) +- Explicit conversion example in docs should use `template get` [\#4038](https://github.com/nlohmann/json/issues/4038) +- Improve wording of parse\_error exception [\#4037](https://github.com/nlohmann/json/issues/4037) +- Parse error on valid JSON file [\#4028](https://github.com/nlohmann/json/issues/4028) +- Empty JSON object returns size of 1 [\#4027](https://github.com/nlohmann/json/issues/4027) +- Help needed to fix CI [\#4025](https://github.com/nlohmann/json/issues/4025) +- Security vulnerabilities detected: CVE-2022-24439, WS-2022-0438, WS-2022-0437 [\#4020](https://github.com/nlohmann/json/issues/4020) +- multithreading use from\_msgpack leading very slow [\#4016](https://github.com/nlohmann/json/issues/4016) +- Error with sol for Lua: items\(\) is not a recognized container [\#4012](https://github.com/nlohmann/json/issues/4012) +- Parser does not read non ascii characters : ŞÜİĞ [\#4007](https://github.com/nlohmann/json/issues/4007) +- malloc\(\): unaligned fastbin chunk detected [\#3999](https://github.com/nlohmann/json/issues/3999) +- try/catch block doesn't work while accessing const json& array. [\#3998](https://github.com/nlohmann/json/issues/3998) +- a bug about list [\#3995](https://github.com/nlohmann/json/issues/3995) +- heap corruption when i use nlohmann::json::accept function to check a valid json [\#3994](https://github.com/nlohmann/json/issues/3994) +- Exception on gcc but not apple clang [\#3986](https://github.com/nlohmann/json/issues/3986) +- Can't support convert the type? std::string json\_str = R"\({"value": "3.1415"}\)"; float value = j\["value"\].get\\(\); [\#3984](https://github.com/nlohmann/json/issues/3984) +- `#pragma once` not supported with C++20 modules in clang [\#3974](https://github.com/nlohmann/json/issues/3974) +- const array\_t::operator\[\] results in buffer overflow / segv on nullptr on out of bounds access [\#3973](https://github.com/nlohmann/json/issues/3973) +- Set minimal permissions to Github Workflows [\#3971](https://github.com/nlohmann/json/issues/3971) +- Parsing array error [\#3968](https://github.com/nlohmann/json/issues/3968) +- why I can return tuple as json? [\#3961](https://github.com/nlohmann/json/issues/3961) +- type must be number, but is null [\#3956](https://github.com/nlohmann/json/issues/3956) +- Class Composition of json members produces incorrect json when constructing with initialization list [\#3955](https://github.com/nlohmann/json/issues/3955) +- exit without error message [\#3948](https://github.com/nlohmann/json/issues/3948) +- NLOHMANN\_DEFINE\_TYPE\_INTRUSIVE doesn't work with "json\_fwd.hpp" [\#3946](https://github.com/nlohmann/json/issues/3946) +- Dangerous use of pull\_request\_target [\#3945](https://github.com/nlohmann/json/issues/3945) +- Test \#7: test-bjdata\_cpp11 ............................\*\*\*Failed [\#3941](https://github.com/nlohmann/json/issues/3941) +- Memory leak detection with basic usage of NLOHMANN\_JSON\_SERIALIZE\_ENUM [\#3939](https://github.com/nlohmann/json/issues/3939) +- Parse doesnt work [\#3936](https://github.com/nlohmann/json/issues/3936) +- Clean up badges [\#3935](https://github.com/nlohmann/json/issues/3935) +- \[json.exception.type\_error.305\] cannot use operator\[\] with a string argument with array [\#3931](https://github.com/nlohmann/json/issues/3931) +- GCC 13 build failures [\#3927](https://github.com/nlohmann/json/issues/3927) +- Exception throw even though code is inside try/catch [\#3926](https://github.com/nlohmann/json/issues/3926) +- Please fix failing tests [\#3923](https://github.com/nlohmann/json/issues/3923) +- Security vulnerability in dependency: future 0.18.2 [\#3922](https://github.com/nlohmann/json/issues/3922) +- json pretty printer causes python exceptions on non-json types [\#3919](https://github.com/nlohmann/json/issues/3919) +- how does a normal basic\_json\<\> object cuase assertion `false` [\#3918](https://github.com/nlohmann/json/issues/3918) +- The library can not parse JSON generate by Chome DevTools Protocol [\#3903](https://github.com/nlohmann/json/issues/3903) +- Typo in `cmake/test.cmake` [\#3902](https://github.com/nlohmann/json/issues/3902) +- Parser adds wrapping array when compiled with GCC [\#3897](https://github.com/nlohmann/json/issues/3897) +- when i use for\(auto iter& : jsonObject\) it occure some error [\#3893](https://github.com/nlohmann/json/issues/3893) +- Check Drone CI [\#3890](https://github.com/nlohmann/json/issues/3890) +- Json::accept\(std::ifstream\) [\#3884](https://github.com/nlohmann/json/issues/3884) +- \[json.exception.parse\_error.101\] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '\[', '{', or a literal [\#3882](https://github.com/nlohmann/json/issues/3882) +- Memory leak when exception is thrown in adl\_serializer::to\_json [\#3881](https://github.com/nlohmann/json/issues/3881) +- building with cmake [\#3880](https://github.com/nlohmann/json/issues/3880) +- \[json.exception.type\_error.316\] invalid UTF-8 byte at index 0: 0xB6 [\#3879](https://github.com/nlohmann/json/issues/3879) +- Visual Studio 2015 C2664 error std::pair\< [\#3867](https://github.com/nlohmann/json/issues/3867) +- I want the data field to be empty serialized and deserialized to each other [\#3866](https://github.com/nlohmann/json/issues/3866) +- Generated natvis is invalid XML [\#3858](https://github.com/nlohmann/json/issues/3858) +- Json Arrays have inconsistent nesting levels across different OSs [\#3854](https://github.com/nlohmann/json/issues/3854) +- Occur error when parse character '\' [\#3844](https://github.com/nlohmann/json/issues/3844) +- Proccess crash as soon as I parse json [\#3843](https://github.com/nlohmann/json/issues/3843) +- json::parse and constructor with the same json generates different type [\#3842](https://github.com/nlohmann/json/issues/3842) +- json::accept return false on valid JSON [\#3838](https://github.com/nlohmann/json/issues/3838) +- decode\(state, codep, byte\) generates warnings. [\#3837](https://github.com/nlohmann/json/issues/3837) +- Arithmetic operators are not working as expected [\#3832](https://github.com/nlohmann/json/issues/3832) +- array\_index does not catch std::invalid\_argument exception from std::stoull [\#3831](https://github.com/nlohmann/json/issues/3831) +- 3.11.2: test suite is failing in two units [\#3828](https://github.com/nlohmann/json/issues/3828) +- Compile Error on g++ using get\(\) function [\#3827](https://github.com/nlohmann/json/issues/3827) +- nlohmann::json::parse can't handle the "€" symbol. [\#3825](https://github.com/nlohmann/json/issues/3825) +- When reading a non-existent key, I unexpectedly get a value. Is it the wrong way I use it? [\#3811](https://github.com/nlohmann/json/issues/3811) +- Code analysis warning string\_concat.hpp C26800: Use of a moved from object [\#3805](https://github.com/nlohmann/json/issues/3805) +- The code used to build with 3.10.2 but fails now [\#3804](https://github.com/nlohmann/json/issues/3804) +- Inconsistent Behaviour of NaN & Null Values [\#3799](https://github.com/nlohmann/json/issues/3799) +- json\_fwd.hpp doesn't work [\#3796](https://github.com/nlohmann/json/issues/3796) +- Compilation fails in MSVC 22 [\#3787](https://github.com/nlohmann/json/issues/3787) +- parsing json with missing key throws exception [\#3784](https://github.com/nlohmann/json/issues/3784) +- Allow to disable inline namespaces completley [\#3746](https://github.com/nlohmann/json/issues/3746) +- double free or Assertion failed! [\#3729](https://github.com/nlohmann/json/issues/3729) +- Amalgated json\_fwd.hpp not included in include.zip [\#3727](https://github.com/nlohmann/json/issues/3727) +- INT64\_MIN/MAX not defined for newer g++ [\#3722](https://github.com/nlohmann/json/issues/3722) +- Compilation error with JSON\_DIAGNOSTICS enabled [\#3717](https://github.com/nlohmann/json/issues/3717) +- class-level enum not serialized as string via NLOHMANN\_JSON\_SERIALIZE\_ENUM [\#3715](https://github.com/nlohmann/json/issues/3715) +- Local copy given by operator\[\] or at\(\) [\#3704](https://github.com/nlohmann/json/issues/3704) +- nlohmann::to\_json method not acting as expected with nlohmann::adl\_serializer specialization. [\#3340](https://github.com/nlohmann/json/issues/3340) +- braced-init-list: array vs other constructor [\#2583](https://github.com/nlohmann/json/issues/2583) + +- JSON for Modern C++ 3.11.3 [\#4222](https://github.com/nlohmann/json/pull/4222) ([nlohmann](https://github.com/nlohmann)) +- Update documentation for the next release [\#4216](https://github.com/nlohmann/json/pull/4216) ([nlohmann](https://github.com/nlohmann)) +- Fix failing CI checks [\#4215](https://github.com/nlohmann/json/pull/4215) ([colbychaskell](https://github.com/colbychaskell)) +- Fix CI \(again\) [\#4196](https://github.com/nlohmann/json/pull/4196) ([nlohmann](https://github.com/nlohmann)) +- fix cmake header path in install with custom CMAKE\_INSTALL\_INCLUDEDIR [\#4194](https://github.com/nlohmann/json/pull/4194) ([bebuch](https://github.com/bebuch)) +- Add more specific error message when attempting to parse empty input [\#4180](https://github.com/nlohmann/json/pull/4180) ([colbychaskell](https://github.com/colbychaskell)) +- Fix char\_traits deprecation warning [\#4179](https://github.com/nlohmann/json/pull/4179) ([colbychaskell](https://github.com/colbychaskell)) +- Fix MinGW CI [\#4175](https://github.com/nlohmann/json/pull/4175) ([scribam](https://github.com/scribam)) +- Fix spellcheck issue [\#4173](https://github.com/nlohmann/json/pull/4173) ([mwestphal](https://github.com/mwestphal)) +- Fix source highlighting in user defined type macros docs [\#4169](https://github.com/nlohmann/json/pull/4169) ([ZeronSix](https://github.com/ZeronSix)) +- Fix deprecation warning [\#4161](https://github.com/nlohmann/json/pull/4161) ([nlohmann](https://github.com/nlohmann)) +- Fix CI [\#4160](https://github.com/nlohmann/json/pull/4160) ([nlohmann](https://github.com/nlohmann)) +- Update index.md [\#4159](https://github.com/nlohmann/json/pull/4159) ([miny1233](https://github.com/miny1233)) +- Update index.md [\#4149](https://github.com/nlohmann/json/pull/4149) ([HO-COOH](https://github.com/HO-COOH)) +- Correct a typo in serve\_header/README.md [\#4143](https://github.com/nlohmann/json/pull/4143) ([felixonmars](https://github.com/felixonmars)) +- Fixed init-list construction when size\_type is not int [\#4140](https://github.com/nlohmann/json/pull/4140) ([tomalakgeretkal](https://github.com/tomalakgeretkal)) +- Update CODEOWNERS [\#4126](https://github.com/nlohmann/json/pull/4126) ([tarolling](https://github.com/tarolling)) +- Accept NEW CMake policies up to CMake 3.14 [\#4112](https://github.com/nlohmann/json/pull/4112) ([craigscott-crascit](https://github.com/craigscott-crascit)) +- Fix typo in afl\_driver.cpp [\#4109](https://github.com/nlohmann/json/pull/4109) ([eltociear](https://github.com/eltociear)) +- Capture exceptions by const& in docs. [\#4099](https://github.com/nlohmann/json/pull/4099) ([iwanders](https://github.com/iwanders)) +- Fix CI, again [\#4083](https://github.com/nlohmann/json/pull/4083) ([nlohmann](https://github.com/nlohmann)) +- Fix Clang-Tidy warnings [\#4047](https://github.com/nlohmann/json/pull/4047) ([nlohmann](https://github.com/nlohmann)) +- Fix compile error with \_HAS\_STATIC\_RTTI=0 [\#4046](https://github.com/nlohmann/json/pull/4046) ([ALF-ONE](https://github.com/ALF-ONE)) +- Add to CONTRIBUTING.md that `make pretty` is required for test updates. [\#4045](https://github.com/nlohmann/json/pull/4045) ([gregmarr](https://github.com/gregmarr)) +- Added to tests the file unit-algorithm.cpp \(c++ 11\) functions from algorithm library [\#4044](https://github.com/nlohmann/json/pull/4044) ([Tomerkm](https://github.com/Tomerkm)) +- Use template get instead of get in examples [\#4039](https://github.com/nlohmann/json/pull/4039) ([tusooa](https://github.com/tusooa)) +- Support Apple's Swift Package Manager [\#4010](https://github.com/nlohmann/json/pull/4010) ([aleksproger](https://github.com/aleksproger)) +- Add Vcpkg port version badge [\#3988](https://github.com/nlohmann/json/pull/3988) ([njakob](https://github.com/njakob)) +- Fix CI + new Doctest [\#3985](https://github.com/nlohmann/json/pull/3985) ([nlohmann](https://github.com/nlohmann)) +- Set minimal permissions to Github Workflows [\#3972](https://github.com/nlohmann/json/pull/3972) ([joycebrum](https://github.com/joycebrum)) +- Refactor amalgamation workflow to avoid dangerous use of pull\_request\_target [\#3969](https://github.com/nlohmann/json/pull/3969) ([joycebrum](https://github.com/joycebrum)) +- Fix typo in test.cmake [\#3951](https://github.com/nlohmann/json/pull/3951) ([theevilone45](https://github.com/theevilone45)) +- tests/unit-iterators2: use std::ranges::equals for range comparisons [\#3950](https://github.com/nlohmann/json/pull/3950) ([ArsenArsen](https://github.com/ArsenArsen)) +- 3935, removed lgtm badge and added Cirrus CI badge [\#3937](https://github.com/nlohmann/json/pull/3937) ([haadfida](https://github.com/haadfida)) +- ⬆️ Bump future from 0.18.2 to 0.18.3 in /docs/mkdocs [\#3934](https://github.com/nlohmann/json/pull/3934) ([dependabot[bot]](https://github.com/apps/dependabot)) +- Change 2022 to 2023 [\#3932](https://github.com/nlohmann/json/pull/3932) ([floriansegginger](https://github.com/floriansegginger)) +- PrettyPrinter: Check if match is valid before accessing group [\#3920](https://github.com/nlohmann/json/pull/3920) ([Finkman](https://github.com/Finkman)) +- Fix CI issues [\#3906](https://github.com/nlohmann/json/pull/3906) ([barcode](https://github.com/barcode)) +- Prevent memory leak when exception is thrown in adl\_serializer::to\_json [\#3901](https://github.com/nlohmann/json/pull/3901) ([barcode](https://github.com/barcode)) +- custom allocators: define missing 'rebind' type [\#3895](https://github.com/nlohmann/json/pull/3895) ([trofi](https://github.com/trofi)) +- Try old MinGW script [\#3892](https://github.com/nlohmann/json/pull/3892) ([nlohmann](https://github.com/nlohmann)) +- Upgrade Python packages [\#3891](https://github.com/nlohmann/json/pull/3891) ([nlohmann](https://github.com/nlohmann)) +- Fix warning about moved from object [\#3889](https://github.com/nlohmann/json/pull/3889) ([nlohmann](https://github.com/nlohmann)) +- Remove a magic number [\#3888](https://github.com/nlohmann/json/pull/3888) ([nlohmann](https://github.com/nlohmann)) +- Add migration guide [\#3887](https://github.com/nlohmann/json/pull/3887) ([nlohmann](https://github.com/nlohmann)) +- Clang 15 [\#3876](https://github.com/nlohmann/json/pull/3876) ([nlohmann](https://github.com/nlohmann)) +- Bump certifi from 2022.6.15 to 2022.12.7 in /docs/mkdocs [\#3872](https://github.com/nlohmann/json/pull/3872) ([dependabot[bot]](https://github.com/apps/dependabot)) +- Fix natvis XML [\#3863](https://github.com/nlohmann/json/pull/3863) ([nlohmann](https://github.com/nlohmann)) +- Fix pipeline [\#3862](https://github.com/nlohmann/json/pull/3862) ([nlohmann](https://github.com/nlohmann)) +- Add CIFuzz CI GitHub action [\#3845](https://github.com/nlohmann/json/pull/3845) ([DavidKorczynski](https://github.com/DavidKorczynski)) +- Add serialization-only user defined type macros [\#3816](https://github.com/nlohmann/json/pull/3816) ([ZeronSix](https://github.com/ZeronSix)) +- Bump joblib from 1.1.0 to 1.2.0 in /docs/mkdocs [\#3781](https://github.com/nlohmann/json/pull/3781) ([dependabot[bot]](https://github.com/apps/dependabot)) +- Fix some typos for n-dimensional arrays [\#3767](https://github.com/nlohmann/json/pull/3767) ([striezel](https://github.com/striezel)) +- Fix 'declaration hides global declaration' warning [\#3751](https://github.com/nlohmann/json/pull/3751) ([falbrechtskirchinger](https://github.com/falbrechtskirchinger)) +- Fix typos in .md files [\#3748](https://github.com/nlohmann/json/pull/3748) ([tocic](https://github.com/tocic)) +- Update Codacy link [\#3740](https://github.com/nlohmann/json/pull/3740) ([nlohmann](https://github.com/nlohmann)) +- Add missing files to release artifacts [\#3728](https://github.com/nlohmann/json/pull/3728) ([falbrechtskirchinger](https://github.com/falbrechtskirchinger)) +- Add dark mode toggle to documentation [\#3726](https://github.com/nlohmann/json/pull/3726) ([falbrechtskirchinger](https://github.com/falbrechtskirchinger)) +- Add clang-tools to required tools for ci\_static\_analysis\_clang [\#3724](https://github.com/nlohmann/json/pull/3724) ([nlohmann](https://github.com/nlohmann)) +- Replace limit macros with std::numeric\_limits [\#3723](https://github.com/nlohmann/json/pull/3723) ([falbrechtskirchinger](https://github.com/falbrechtskirchinger)) +- Add missing \ include [\#3719](https://github.com/nlohmann/json/pull/3719) ([falbrechtskirchinger](https://github.com/falbrechtskirchinger)) +- Add Bazel build support [\#3709](https://github.com/nlohmann/json/pull/3709) ([Vertexwahn](https://github.com/Vertexwahn)) +- Use official Clang/GCC containers [\#3703](https://github.com/nlohmann/json/pull/3703) ([nlohmann](https://github.com/nlohmann)) +- Add 'Check amalgamation' workflow [\#3693](https://github.com/nlohmann/json/pull/3693) ([falbrechtskirchinger](https://github.com/falbrechtskirchinger)) +- Allow custom base class as node customization point [\#3110](https://github.com/nlohmann/json/pull/3110) ([barcode](https://github.com/barcode)) + +## [v3.11.2](https://github.com/nlohmann/json/releases/tag/v3.11.2) (2022-08-12) + +[Full Changelog](https://github.com/nlohmann/json/compare/v3.11.1...v3.11.2) - MSVC natvis visualizer does not work after introduction of inline ABI namespace [\#3696](https://github.com/nlohmann/json/issues/3696) - The use of parenthesis gives compilation errors in some situations [\#3682](https://github.com/nlohmann/json/issues/3682) @@ -145,7 +476,7 @@ All notable changes to this project will be documented in this file. This projec - Fix warning [\#3634](https://github.com/nlohmann/json/pull/3634) ([nlohmann](https://github.com/nlohmann)) - Add license header to new files [\#3633](https://github.com/nlohmann/json/pull/3633) ([nlohmann](https://github.com/nlohmann)) - Add a unit test including windows.h [\#3631](https://github.com/nlohmann/json/pull/3631) ([falbrechtskirchinger](https://github.com/falbrechtskirchinger)) -- Fixed latest build error in msvc platform [\#3630](https://github.com/nlohmann/json/pull/3630) ([KsaNL](https://github.com/KsaNL)) +- Fixed latest build error in msvc platform [\#3630](https://github.com/nlohmann/json/pull/3630) ([Lioncky](https://github.com/Lioncky)) - Add regression tests for \#3204 and \#3333 [\#3629](https://github.com/nlohmann/json/pull/3629) ([falbrechtskirchinger](https://github.com/falbrechtskirchinger)) - Fix patch::add creating nonexistent parents [\#3628](https://github.com/nlohmann/json/pull/3628) ([falbrechtskirchinger](https://github.com/falbrechtskirchinger)) - Adjust JSON Pointer examples [\#3622](https://github.com/nlohmann/json/pull/3622) ([nlohmann](https://github.com/nlohmann)) @@ -178,7 +509,7 @@ All notable changes to this project will be documented in this file. This projec - Use REUSE framework [\#3546](https://github.com/nlohmann/json/pull/3546) ([nlohmann](https://github.com/nlohmann)) - Use `std::iterator_traits` to extract `iterator_category` [\#3544](https://github.com/nlohmann/json/pull/3544) ([Mike-Leo-Smith](https://github.com/Mike-Leo-Smith)) - BJData dimension length can not be string\_t::npos, fix \#3541 [\#3543](https://github.com/nlohmann/json/pull/3543) ([fangq](https://github.com/fangq)) -- Allow disabling default enum conversions [\#3536](https://github.com/nlohmann/json/pull/3536) ([zxey](https://github.com/zxey)) +- Allow disabling default enum conversions [\#3536](https://github.com/nlohmann/json/pull/3536) ([richardhozak](https://github.com/richardhozak)) - Add to\_json\(\) for std::vector\::reference [\#3534](https://github.com/nlohmann/json/pull/3534) ([falbrechtskirchinger](https://github.com/falbrechtskirchinger)) - CI: Enable 32bit unit test \(3\) [\#3532](https://github.com/nlohmann/json/pull/3532) ([falbrechtskirchinger](https://github.com/falbrechtskirchinger)) - Use new CI image [\#3528](https://github.com/nlohmann/json/pull/3528) ([nlohmann](https://github.com/nlohmann)) @@ -639,7 +970,6 @@ All notable changes to this project will be documented in this file. This projec - array\_index possible out of range [\#2205](https://github.com/nlohmann/json/issues/2205) - Object deserialized as array [\#2204](https://github.com/nlohmann/json/issues/2204) - Sending to a function a reference to a sub-branch [\#2200](https://github.com/nlohmann/json/issues/2200) -- How to Serialize derived class to JSON object? [\#2199](https://github.com/nlohmann/json/issues/2199) - JSON incorrectly serialized [\#2198](https://github.com/nlohmann/json/issues/2198) - Exception Unhandled out\_of\_range error [\#2197](https://github.com/nlohmann/json/issues/2197) - msgpack serialisation : float is treated as 64bit float, not 32bit float. [\#2196](https://github.com/nlohmann/json/issues/2196) @@ -672,7 +1002,6 @@ All notable changes to this project will be documented in this file. This projec - Compilation failure using Clang on Windows [\#1898](https://github.com/nlohmann/json/issues/1898) - Fail to build when including json.hpp as a system include [\#1818](https://github.com/nlohmann/json/issues/1818) - Parsing string into json doesn't preserve the order correctly. [\#1817](https://github.com/nlohmann/json/issues/1817) -- \[C++17\] Allow std::optional to convert to nlohmann::json [\#1749](https://github.com/nlohmann/json/issues/1749) - How can I save json object in file in order? [\#1717](https://github.com/nlohmann/json/issues/1717) - Support for Comments [\#1513](https://github.com/nlohmann/json/issues/1513) - clang compiler: error : unknown type name 'not' [\#1119](https://github.com/nlohmann/json/issues/1119) @@ -1692,7 +2021,7 @@ All notable changes to this project will be documented in this file. This projec - Use a version check to provide backwards comatible CMake imported target names [\#1245](https://github.com/nlohmann/json/pull/1245) ([chuckatkins](https://github.com/chuckatkins)) - Fix issue \#1237 [\#1238](https://github.com/nlohmann/json/pull/1238) ([theodelrieu](https://github.com/theodelrieu)) - Add a get overload taking a parameter. [\#1231](https://github.com/nlohmann/json/pull/1231) ([theodelrieu](https://github.com/theodelrieu)) -- Move lambda out of unevaluated context [\#1230](https://github.com/nlohmann/json/pull/1230) ([mandreyel](https://github.com/mandreyel)) +- Move lambda out of unevaluated context [\#1230](https://github.com/nlohmann/json/pull/1230) ([vimpunk](https://github.com/vimpunk)) - Remove static asserts [\#1228](https://github.com/nlohmann/json/pull/1228) ([theodelrieu](https://github.com/theodelrieu)) - Better error 305 [\#1221](https://github.com/nlohmann/json/pull/1221) ([rivertam](https://github.com/rivertam)) - Fix \#1213 [\#1214](https://github.com/nlohmann/json/pull/1214) ([simnalamburt](https://github.com/simnalamburt)) @@ -1859,8 +2188,8 @@ All notable changes to this project will be documented in this file. This projec - Fix unit tests that were silently skipped or crashed \(depending on the compiler\) [\#1176](https://github.com/nlohmann/json/pull/1176) ([grembo](https://github.com/grembo)) - Refactor/no virtual sax [\#1153](https://github.com/nlohmann/json/pull/1153) ([theodelrieu](https://github.com/theodelrieu)) - Fixed compiler error in VS 2015 for debug mode [\#1151](https://github.com/nlohmann/json/pull/1151) ([sonulohani](https://github.com/sonulohani)) -- Fix links to cppreference named requirements \(formerly concepts\) [\#1144](https://github.com/nlohmann/json/pull/1144) ([jrakow](https://github.com/jrakow)) -- meson: fix include directory [\#1142](https://github.com/nlohmann/json/pull/1142) ([jrakow](https://github.com/jrakow)) +- Fix links to cppreference named requirements \(formerly concepts\) [\#1144](https://github.com/nlohmann/json/pull/1144) ([ghost](https://github.com/ghost)) +- meson: fix include directory [\#1142](https://github.com/nlohmann/json/pull/1142) ([ghost](https://github.com/ghost)) - Feature/unordered map conversion [\#1138](https://github.com/nlohmann/json/pull/1138) ([theodelrieu](https://github.com/theodelrieu)) - fixed compile error for \#1045 [\#1134](https://github.com/nlohmann/json/pull/1134) ([Daniel599](https://github.com/Daniel599)) - test \(non\)equality for alt\_string implementation [\#1130](https://github.com/nlohmann/json/pull/1130) ([agrianius](https://github.com/agrianius)) @@ -1869,7 +2198,7 @@ All notable changes to this project will be documented in this file. This projec - fix typo in README [\#1078](https://github.com/nlohmann/json/pull/1078) ([martin-mfg](https://github.com/martin-mfg)) - Fix typo [\#1058](https://github.com/nlohmann/json/pull/1058) ([dns13](https://github.com/dns13)) - Misc cmake packaging enhancements [\#1048](https://github.com/nlohmann/json/pull/1048) ([chuckatkins](https://github.com/chuckatkins)) -- Fixed incorrect LLVM version number in README [\#1047](https://github.com/nlohmann/json/pull/1047) ([jammehcow](https://github.com/jammehcow)) +- Fixed incorrect LLVM version number in README [\#1047](https://github.com/nlohmann/json/pull/1047) ([jupjohn](https://github.com/jupjohn)) - Fix trivial typo in comment. [\#1043](https://github.com/nlohmann/json/pull/1043) ([coryan](https://github.com/coryan)) - Package Manager: Spack [\#1041](https://github.com/nlohmann/json/pull/1041) ([ax3l](https://github.com/ax3l)) - CMake: 3.8+ is Sufficient [\#1040](https://github.com/nlohmann/json/pull/1040) ([ax3l](https://github.com/ax3l)) @@ -2398,7 +2727,7 @@ All notable changes to this project will be documented in this file. This projec - Fix "not constraint" grammar in docs [\#674](https://github.com/nlohmann/json/pull/674) ([wincent](https://github.com/wincent)) - Add documentation for integration with CMake and hunter [\#671](https://github.com/nlohmann/json/pull/671) ([dan-42](https://github.com/dan-42)) - REFACTOR: rewrite CMakeLists.txt for better inlcude and reuse [\#669](https://github.com/nlohmann/json/pull/669) ([dan-42](https://github.com/dan-42)) -- enable\_testing only if the JSON\_BuildTests is ON [\#666](https://github.com/nlohmann/json/pull/666) ([effolkronium](https://github.com/effolkronium)) +- enable\_testing only if the JSON\_BuildTests is ON [\#666](https://github.com/nlohmann/json/pull/666) ([ilqvya](https://github.com/ilqvya)) - Support moving from rvalues in std::initializer\_list [\#663](https://github.com/nlohmann/json/pull/663) ([himikof](https://github.com/himikof)) - add ensure\_ascii parameter to dump. \#330 [\#654](https://github.com/nlohmann/json/pull/654) ([ryanjmulder](https://github.com/ryanjmulder)) - Rename BuildTests to JSON\_BuildTests [\#652](https://github.com/nlohmann/json/pull/652) ([olegendo](https://github.com/olegendo)) @@ -2917,11 +3246,11 @@ All notable changes to this project will be documented in this file. This projec - Keyword 'inline' is useless when member functions are defined in headers [\#87](https://github.com/nlohmann/json/pull/87) ([ahamez](https://github.com/ahamez)) - Remove useless typename [\#86](https://github.com/nlohmann/json/pull/86) ([ahamez](https://github.com/ahamez)) - Avoid warning with Xcode's clang [\#85](https://github.com/nlohmann/json/pull/85) ([ahamez](https://github.com/ahamez)) -- Fix typos [\#73](https://github.com/nlohmann/json/pull/73) ([aqnouch](https://github.com/aqnouch)) +- Fix typos [\#73](https://github.com/nlohmann/json/pull/73) ([maqnouch](https://github.com/maqnouch)) - Replace `default_callback` function with `nullptr` and check for null… [\#72](https://github.com/nlohmann/json/pull/72) ([aburgh](https://github.com/aburgh)) - support enum [\#71](https://github.com/nlohmann/json/pull/71) ([likebeta](https://github.com/likebeta)) - Fix performance regression introduced with the parsing callback feature. [\#69](https://github.com/nlohmann/json/pull/69) ([aburgh](https://github.com/aburgh)) -- Improve the implementations of the comparission-operators [\#63](https://github.com/nlohmann/json/pull/63) ([Florianjw](https://github.com/Florianjw)) +- Improve the implementations of the comparission-operators [\#63](https://github.com/nlohmann/json/pull/63) ([Fiona-J-W](https://github.com/Fiona-J-W)) - Fix compilation of json\_unit with GCC 5 [\#59](https://github.com/nlohmann/json/pull/59) ([dkopecek](https://github.com/dkopecek)) - Parse streams incrementally. [\#40](https://github.com/nlohmann/json/pull/40) ([aburgh](https://github.com/aburgh)) - Feature/small float serialization [\#38](https://github.com/nlohmann/json/pull/38) ([jrandall](https://github.com/jrandall)) diff --git a/FILES.md b/FILES.md new file mode 100644 index 0000000000..91326ab6ad --- /dev/null +++ b/FILES.md @@ -0,0 +1,239 @@ +# Supporting files + +This file describes the source for supporting files; that is, files that are not part of the library, but define the infrastructure and other aspects of the project. + +- [Continuous Integration](#continuous-integration) +- [GitHub](#github) +- [REUSE](#reuse) +- [Package Managers](#package-managers) + +## Continuous Integration + +### `.cirrus.yml` + +Configuration file for the pipeline at [Cirrus CI](https://cirrus-ci.com/github/nlohmann/json). + +Further documentation: + +- [Writing tasks](https://cirrus-ci.org/guide/writing-tasks/) + +> [!IMPORTANT] +> The filename `.cirrus.yml` and position (root of the repository) are predetermined by Cirrus CI. + +### `.github/external_ci/appveyor.yml` + +Configuration for the pipelines at [AppVeyor](https://ci.appveyor.com/project/nlohmann/json). + +Further documentation: + +- [appveyor.yml reference](https://www.appveyor.com/docs/appveyor-yml/) + +> [!NOTE] +> The filename can be freely configured in the AppVeyor project. + +## GitHub + +### `CITATION.cff` + +A file to configure the citation for the repository which is displayed in the sidebar of the project. + +Further documentation: + +- [About CITATION files](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-citation-files) + +> [!IMPORTANT] +> The filename `CITATION.cff` and position (root of the repository) are predetermined by GitHub. + +### `.github/CODE_OF_CONDUCT.md` + +The code of conduct for the project. This is the Markdown version of the [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). The code of conduct is linked on the [Community Standards](https://github.com/nlohmann/json/community) page and is mentioned by the Sentiment Bot. + +Further documentation: + +- [Adding a code of conduct to your project](https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/adding-a-code-of-conduct-to-your-project) + +> [!IMPORTANT] +> The filename `.github/CODE_OF_CONDUCT.md` is predetermined by GitHub. + +> [!NOTE] +> The file is part of the documentation and is included in `docs/mkdocs/docs/community/code_of_conduct.md`. + +### `.github/CODEOWNERS` + +The code owners file for the project which is used to select reviewers for new pull requests. + +Further documentation: + +- [About code owners](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners) + +> [!IMPORTANT] +> The filename `.github/CODEOWNERS` is predetermined by GitHub. + +### `.github/config.yml` + +Configuration file for [probot](https://probot.github.io/apps/), in particular the [Sentiment Bot](https://probot.github.io/apps/sentiment-bot/) and the [Request Info](https://probot.github.io/apps/request-info/). + +> [!IMPORTANT] +> The filename `.github/config.yml` is predetermined by probot. + +### `.github/CONTRIBUTING.md` + +The contribution guidelines which are linked in the [Community Standards](https://github.com/nlohmann/json/community) and at . + +Further documentation: + +- [Setting guidelines for repository contributors](https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/setting-guidelines-for-repository-contributors) + +> [!IMPORTANT] +> The filename `.github/CONTRIBUTING.md` is predetermined by GitHub. + +> [!NOTE] +> The file is part of the documentation and is included in `docs/mkdocs/docs/community/contribution_guidelines.md`. + +### `.github/dependabot.yml` + +The configuration of [dependabot](https://github.com/dependabot) which ensures the dependencies (GitHub actions and Python packages used in the CI) remain up to date. + +Further documentation: + +- [Configuring Dependabot security updates](https://docs.github.com/en/code-security/dependabot/dependabot-security-updates/configuring-dependabot-security-updates) + +> [!IMPORTANT] +> The filename `.github/dependabot.yml` is predetermined by GitHub. + +### `.github/FUNDING.yml` + +A file to configure the sponsor button of the repository which is displayed in the sidebar of the project. + +Further documentation: + +- [Displaying a sponsor button in your repository](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository) + +> [!IMPORTANT] +> The filename `.github/FUNDING.yml` is predetermined by GitHub. + +### `.github/ISSUE_TEMPLATE/bug.yaml` + +Issue form template for bugs. + +Further documentation: + +- [Configuring issue templates for your repository](https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository) + +> [!IMPORTANT] +> The folder `.github/ISSUE_TEMPLATE` is predetermined by GitHub. + +### `.github/ISSUE_TEMPLATE/config.yaml` + +Issue template chooser configuration. The file is used to configure the dialog when a new issue is created. + +Further documentation: + +- [Configuring issue templates for your repository](https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository) + +> [!IMPORTANT] +> The filename `.github/ISSUE_TEMPLATE/config.yaml` is predetermined by GitHub. + +### `.github/labeler.yml` + +Configuration file for the "Pull Request Labeler" workflow defined in `workflows/labeler.yml`. This file defines rules how labels are assigned to pull requests based on which files are changed. + +Further documentation: + +- [Label manager for PRs and issues based on configurable conditions](https://github.com/srvaroa/labeler) + +> [!NOTE] +> The filename defaults to `.github/labeler.yml` and can be configured in the workflow. + +### `.github/PULL_REQUEST_TEMPLATE.md` + +The pull request template which prefills new pull requests. + +Further documentation: + +- [Creating a pull request template for your repository](https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/creating-a-pull-request-template-for-your-repository) + +> [!IMPORTANT] +> The filename `.github/PULL_REQUEST_TEMPLATE.md` is predetermined by GitHub. + +### `.github/SECURITY.md` + +The goal is to describe how to securely report security vulnerabilities for this repository. The security policy is linked at . + +Further documentation: + +- [Adding a security policy to your repository](https://docs.github.com/en/code-security/getting-started/adding-a-security-policy-to-your-repository) + +> [!IMPORTANT] +> The filename `.github/SECURITY.yml` is predetermined by GitHub. + +> [!NOTE] +> The file is part of the documentation and is included in `docs/mkdocs/docs/community/security_policy.md`. + +### `LICENSE.MIT` + +The license of the project. + +Further documentation: + +- [Adding a license to a repository](https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/adding-a-license-to-a-repository) + +> [!IMPORTANT] +> The filename `LICENSE.MIT` is partly predetermined by GitHub. The root filename must be `LICENSE`. + +## REUSE + +### `.reuse/dep5` + +The file defines the licenses of certain third-party components in the repository. The root `Makefile` contains a target `reuse` that checks for compliance. + +Further documentation: + +- [DEP5](https://reuse.software/spec-3.2/#dep5-deprecated) +- [reuse command-line tool](https://pypi.org/project/reuse/) +- [documentation of linting](https://reuse.readthedocs.io/en/stable/man/reuse-lint.html) +- [REUSE](http://reuse.software) + +> [!IMPORTANT] +> The filename `.reuse/dep5` is predetermined by REUSE. Alternatively, a `REUSE.toml` file can be used. + +### `.reuse/templates` + +Copyright header templates for source files. The root `Makefile` contains a target `reuse` that updates copyright headers with the templates. + +Further information: + +- [reuse command-line tool](https://pypi.org/project/reuse/) +- [documentation on templates](https://reuse.readthedocs.io/en/stable/man/reuse-annotate.html#cmdoption-t) +- [REUSE](http://reuse.software) + +> [!IMPORTANT] +> The folder name `.reuse/templates` is predetermined by REUSE. + +### `LICENSES` + +A folder that contains every license of all license files (library and third-party code). + +Further documentation: + +- [REUSE specification](https://reuse.software/spec-3.3/) + +> [!IMPORTANT] +> The folder name `LICENSES` is predetermined by REUSE. + + +## Package Managers + +### `BUILD.bazel` + +The file can be updated by calling + +```shell +make BUILD.bazel +``` + +### `meson.build` + +### `Package.swift` + +### `WORKSPACE.bazel` diff --git a/LICENSE.MIT b/LICENSE.MIT index 1c1f7a690d..770016601b 100644 --- a/LICENSE.MIT +++ b/LICENSE.MIT @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2013-2022 Niels Lohmann +Copyright (c) 2013-2026 Niels Lohmann Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/LICENSES/CC0-1.0.txt b/LICENSES/CC0-1.0.txt new file mode 100644 index 0000000000..0e259d42c9 --- /dev/null +++ b/LICENSES/CC0-1.0.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 0000000000..51754e7a3a --- /dev/null +++ b/MODULE.bazel @@ -0,0 +1,7 @@ +module( + name = "nlohmann_json", + compatibility_level = 1, +) + +bazel_dep(name = "rules_cc", version = "0.0.17") +bazel_dep(name = "rules_license", version = "1.0.0") diff --git a/Makefile b/Makefile index 20c17ec962..fa2e5a54ee 100644 --- a/Makefile +++ b/Makefile @@ -43,15 +43,6 @@ all: @echo "run_benchmarks - build and run benchmarks" -########################################################################## -# documentation tests -########################################################################## - -# compile example files and check output -doctest: - $(MAKE) check_output -C docs - - ########################################################################## # benchmarks ########################################################################## @@ -142,32 +133,20 @@ pvs_studio: # Code format and source amalgamation ########################################################################## +ASTYLE=tools/astyle/venv/bin/astyle + +install_astyle: + @test -d tools/astyle/venv || python3 -mvenv tools/astyle/venv ; tools/astyle/venv/bin/pip3 install --quiet --upgrade pip + @test -f $(ASTYLE) || tools/astyle/venv/bin/pip3 install --quiet -r tools/astyle/requirements.txt + @$(ASTYLE) --version + # call the Artistic Style pretty printer on all source files -pretty: - astyle \ - --style=allman \ - --indent=spaces=4 \ - --indent-modifiers \ - --indent-switches \ - --indent-preproc-block \ - --indent-preproc-define \ - --indent-col1-comments \ - --pad-oper \ - --pad-header \ - --align-pointer=type \ - --align-reference=type \ - --add-braces \ - --convert-tabs \ - --close-templates \ - --lineend=linux \ - --preserve-date \ - --suffix=none \ - --formatted \ - $(SRCS) $(TESTS_SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) docs/examples/*.cpp +pretty: install_astyle + $(ASTYLE) --project=tools/astyle/.astylerc $(SRCS) $(TESTS_SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) docs/mkdocs/docs/examples/*.cpp # call the Clang-Format on all source files pretty_format: - for FILE in $(SRCS) $(TESTS_SRCS) $(AMALGAMATED_FILE) docs/examples/*.cpp; do echo $$FILE; clang-format -i $$FILE; done + for FILE in $(SRCS) $(TESTS_SRCS) $(AMALGAMATED_FILE) docs/mkdocs/docs/examples/*.cpp; do echo $$FILE; clang-format -i $$FILE; done # create single header files and pretty print amalgamate: $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) @@ -215,7 +194,7 @@ ChangeLog.md: # Release files ########################################################################## -# Create a tar.gz archive that contains sufficient files to be used as CMake project (e.g., using FetchContent). The +# Create a tar.xz archive that contains sufficient files to be used as CMake project (e.g., using FetchContent). The # archive is created according to the advices of . json.tar.xz: mkdir json @@ -279,6 +258,9 @@ serve_header: ########################################################################## reuse: - pipx run reuse addheader --recursive single_include include -tjson --license MIT --copyright "Niels Lohmann " --year "2013-2022" - pipx run reuse addheader $(TESTS_SRCS) --style=c -tjson_support --license MIT --copyright "Niels Lohmann " --year "2013-2022" + pipx run reuse annotate --recursive single_include include -tjson --license MIT --copyright "Niels Lohmann " --year "2013-2026" --merge-copyrights + pipx run reuse annotate $(TESTS_SRCS) -tjson_support --license MIT --copyright "Niels Lohmann " --year "2013-2026" --merge-copyrights pipx run reuse lint + +spdx: + pipx run reuse spdx --output nlohmann_json.spdx --creator-person "Niels Lohmann" --add-license-concluded diff --git a/Package.swift b/Package.swift index 2f9c4a1f4d..2f1e654a2c 100644 --- a/Package.swift +++ b/Package.swift @@ -6,7 +6,7 @@ import PackageDescription let package = Package( name: "nlohmann-json", platforms: [ - .iOS(.v12), .macOS(.v10_13), .tvOS(.v12), .watchOS(.v4) + .iOS(.v12), .macOS(.v10_13), .tvOS(.v12), .watchOS(.v4), .visionOS(.v1) ], products: [ .library(name: "json", targets: ["json"]) diff --git a/README.md b/README.md index 9f8ff17826..dd10272693 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![JSON for Modern C++](docs/json.gif)](https://github.com/nlohmann/json/releases) +[![JSON for Modern C++](docs/mkdocs/docs/images/json.gif)](https://github.com/nlohmann/json/releases) [![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk/branch/develop?svg=true)](https://ci.appveyor.com/project/nlohmann/json) [![Ubuntu](https://github.com/nlohmann/json/workflows/Ubuntu/badge.svg)](https://github.com/nlohmann/json/actions?query=workflow%3AUbuntu) @@ -6,19 +6,20 @@ [![Windows](https://github.com/nlohmann/json/workflows/Windows/badge.svg)](https://github.com/nlohmann/json/actions?query=workflow%3AWindows) [![Coverage Status](https://coveralls.io/repos/github/nlohmann/json/badge.svg?branch=develop)](https://coveralls.io/github/nlohmann/json?branch=develop) [![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json) -[![Codacy Badge](https://app.codacy.com/project/badge/Grade/e0d1a9d5d6fd46fcb655c4cb930bb3e8)](https://www.codacy.com/gh/nlohmann/json/dashboard?utm_source=github.com&utm_medium=referral&utm_content=nlohmann/json&utm_campaign=Badge_Grade) +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/e0d1a9d5d6fd46fcb655c4cb930bb3e8)](https://app.codacy.com/gh/nlohmann/json/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [![Cirrus CI](https://api.cirrus-ci.com/github/nlohmann/json.svg)](https://cirrus-ci.com/github/nlohmann/json) [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/json.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:json) [![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/1mp10JbaANo6FUc7) [![Documentation](https://img.shields.io/badge/docs-mkdocs-blue.svg)](https://json.nlohmann.me) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT) [![GitHub Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases) -[![Vcpkg Version](https://img.shields.io/vcpkg/v/nlohmann-json)](https://vcpkg.link/ports/nlohmann-json) [![Packaging status](https://repology.org/badge/tiny-repos/nlohmann-json.svg)](https://repology.org/project/nlohmann-json/versions) [![GitHub Downloads](https://img.shields.io/github/downloads/nlohmann/json/total)](https://github.com/nlohmann/json/releases) [![GitHub Issues](https://img.shields.io/github/issues/nlohmann/json.svg)](https://github.com/nlohmann/json/issues) [![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/nlohmann/json.svg)](https://isitmaintained.com/project/nlohmann/json "Average time to resolve an issue") [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/289/badge)](https://bestpractices.coreinfrastructure.org/projects/289) +[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/nlohmann/json/badge)](https://scorecard.dev/viewer/?uri=github.com/nlohmann/json) +[![Backup Status](https://app.cloudback.it/badge/nlohmann/json)](https://cloudback.it) [![GitHub Sponsors](https://img.shields.io/badge/GitHub-Sponsors-ff69b4)](https://github.com/sponsors/nlohmann) [![REUSE status](https://api.reuse.software/badge/github.com/nlohmann/json)](https://api.reuse.software/info/github.com/nlohmann/json) [![Discord](https://img.shields.io/discord/1003743314341793913)](https://discord.gg/6mrGXKvX7y) @@ -26,10 +27,11 @@ - [Design goals](#design-goals) - [Sponsors](#sponsors) - [Support](#support) ([documentation](https://json.nlohmann.me), [FAQ](https://json.nlohmann.me/home/faq/), [discussions](https://github.com/nlohmann/json/discussions), [API](https://json.nlohmann.me/api/basic_json/), [bug issues](https://github.com/nlohmann/json/issues)) +- [Quick reference](#quick-reference) - [Examples](#examples) - [Read JSON from a file](#read-json-from-a-file) - [Creating `json` objects from JSON literals](#creating-json-objects-from-json-literals) - - [JSON as first-class data type](#json-as-first-class-data-type) + - [JSON as a first-class data type](#json-as-a-first-class-data-type) - [Serialization / Deserialization](#serialization--deserialization) - [STL-like access](#stl-like-access) - [Conversion from STL containers](#conversion-from-stl-containers) @@ -39,6 +41,7 @@ - [Conversions to/from arbitrary types](#arbitrary-types-conversions) - [Specializing enum conversion](#specializing-enum-conversion) - [Binary formats (BSON, CBOR, MessagePack, UBJSON, and BJData)](#binary-formats-bson-cbor-messagepack-ubjson-and-bjdata) +- [Customers](#customers) - [Supported compilers](#supported-compilers) - [Integration](#integration) - [CMake](#cmake) @@ -48,7 +51,6 @@ - [Contact](#contact) - [Thanks](#thanks) - [Used third-party tools](#used-third-party-tools) -- [Projects using JSON for Modern C++](#projects-using-json-for-modern-c) - [Notes](#notes) - [Execute unit tests](#execute-unit-tests) @@ -56,11 +58,11 @@ There are myriads of [JSON](https://json.org) libraries out there, and each may even have its reason to exist. Our class had these design goals: -- **Intuitive syntax**. In languages such as Python, JSON feels like a first class data type. We used all the operator magic of modern C++ to achieve the same feeling in your code. Check out the [examples below](#examples) and you'll know what I mean. +- **Intuitive syntax**. In languages such as Python, JSON feels like a first-class data type. We used all the operator magic of modern C++ to achieve the same feeling in your code. Check out the [examples below](#examples) and you'll know what I mean. -- **Trivial integration**. Our whole code consists of a single header file [`json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp). That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings. +- **Trivial integration**. Our whole code consists of a single header file [`json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp). That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings. The library is also included in all popular [package managers](https://json.nlohmann.me/integration/package_managers/). -- **Serious testing**. Our code is heavily [unit-tested](https://github.com/nlohmann/json/tree/develop/tests/src) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](https://valgrind.org) and the [Clang Sanitizers](https://clang.llvm.org/docs/index.html) that there are no memory leaks. [Google OSS-Fuzz](https://github.com/google/oss-fuzz/tree/master/projects/json) additionally runs fuzz tests against all parsers 24/7, effectively executing billions of tests so far. To maintain high quality, the project is following the [Core Infrastructure Initiative (CII) best practices](https://bestpractices.coreinfrastructure.org/projects/289). +- **Serious testing**. Our code is heavily [unit-tested](https://github.com/nlohmann/json/tree/develop/tests/src) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](https://valgrind.org) and the [Clang Sanitizers](https://clang.llvm.org/docs/index.html) that there are no memory leaks. [Google OSS-Fuzz](https://github.com/google/oss-fuzz/tree/master/projects/json) additionally runs fuzz tests against all parsers 24/7, effectively executing billions of tests so far. To maintain high quality, the project is following the [Core Infrastructure Initiative (CII) best practices](https://bestpractices.coreinfrastructure.org/projects/289). See the [quality assurance](https://json.nlohmann.me/community/quality_assurance) overview documentation. Other aspects were not so important to us: @@ -70,7 +72,6 @@ Other aspects were not so important to us: See the [contribution guidelines](https://github.com/nlohmann/json/blob/master/.github/CONTRIBUTING.md#please-dont) for more information. - ## Sponsors You can sponsor this library at [GitHub Sponsors](https://github.com/sponsors/nlohmann). @@ -78,6 +79,7 @@ You can sponsor this library at [GitHub Sponsors](https://github.com/sponsors/nl ### :raising_hand: Priority Sponsor - [Martti Laine](https://github.com/codeclown) +- [Paul Harrington](https://github.com/phrrngtn) ### :label: Named Sponsors @@ -88,30 +90,58 @@ You can sponsor this library at [GitHub Sponsors](https://github.com/sponsors/nl - [Steve Wagner](https://github.com/ciroque) - [Lion Yang](https://github.com/LionNatsu) +### Further support + +The development of the library is further supported by JetBrains by providing free access to their IDE tools. + +[![JetBrains logo.](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg)](https://jb.gg/OpenSourceSupport) + Thanks everyone! ## Support :question: If you have a **question**, please check if it is already answered in the [**FAQ**](https://json.nlohmann.me/home/faq/) or the [**Q&A**](https://github.com/nlohmann/json/discussions/categories/q-a) section. If not, please [**ask a new question**](https://github.com/nlohmann/json/discussions/new) there. -:books: If you want to **learn more** about how to use the library, check out the rest of the [**README**](#examples), have a look at [**code examples**](https://github.com/nlohmann/json/tree/develop/docs/examples), or browse through the [**help pages**](https://json.nlohmann.me). +:books: If you want to **learn more** about how to use the library, check out the rest of the [**README**](#examples), have a look at [**code examples**](https://github.com/nlohmann/json/tree/develop/docs/mkdocs/docs/examples), or browse through the [**help pages**](https://json.nlohmann.me). -:construction: If you want to understand the **API** better, check out the [**API Reference**](https://json.nlohmann.me/api/basic_json/). +:construction: If you want to understand the **API** better, check out the [**API Reference**](https://json.nlohmann.me/api/basic_json/) or have a look at the [quick reference](#quick-reference) below. :bug: If you found a **bug**, please check the [**FAQ**](https://json.nlohmann.me/home/faq/) if it is a known issue or the result of a design decision. Please also have a look at the [**issue list**](https://github.com/nlohmann/json/issues) before you [**create a new issue**](https://github.com/nlohmann/json/issues/new/choose). Please provide as much information as possible to help us understand and reproduce your issue. -There is also a [**docset**](https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/JSON_for_Modern_C%2B%2B) for the documentation browsers [Dash](https://kapeli.com/dash), [Velocity](https://velocity.silverlakesoftware.com), and [Zeal](https://zealdocs.org) that contains the full [documentation](https://json.nlohmann.me) as offline resource. +There is also a [**docset**](https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/JSON_for_Modern_C%2B%2B) for the documentation browsers [Dash](https://kapeli.com/dash), [Velocity](https://velocity.silverlakesoftware.com), and [Zeal](https://zealdocs.org) that contains the full [documentation](https://json.nlohmann.me) as an offline resource. + +## Quick reference + +- **Constructors** [basic_json](https://json.nlohmann.me/api/basic_json/basic_json), [array](https://json.nlohmann.me/api/basic_json/array), [binary](https://json.nlohmann.me/api/basic_json/binary), [object](https://json.nlohmann.me/api/basic_json/object) +- **Object inspection**: [type](https://json.nlohmann.me/api/basic_json/type), [operator value_t](https://json.nlohmann.me/api/basic_json/operator_value_t), [type_name](https://json.nlohmann.me/api/basic_json/type_name), [is_primitive](https://json.nlohmann.me/api/basic_json/is_primitive), [is_structured](https://json.nlohmann.me/api/basic_json/is_structured), [is_null](https://json.nlohmann.me/api/basic_json/is_null), [is_boolean](https://json.nlohmann.me/api/basic_json/is_boolean), [is_number](https://json.nlohmann.me/api/basic_json/is_number), [is_number_integer](https://json.nlohmann.me/api/basic_json/is_number_integer), [is_number_unsigned](https://json.nlohmann.me/api/basic_json/is_number_unsigned), [is_number_float](https://json.nlohmann.me/api/basic_json/is_number_float), [is_object](https://json.nlohmann.me/api/basic_json/is_object), [is_array](https://json.nlohmann.me/api/basic_json/is_array), [is_string](https://json.nlohmann.me/api/basic_json/is_string), [is_binary](https://json.nlohmann.me/api/basic_json/is_binary), [is_discarded](https://json.nlohmann.me/api/basic_json/is_discarded) +- **Value access**; [get](https://json.nlohmann.me/api/basic_json/get), [get_to](https://json.nlohmann.me/api/basic_json/get_to), [get_ptr](https://json.nlohmann.me/api/basic_json/get_ptr), [get_ref](https://json.nlohmann.me/api/basic_json/get_ref), [operator ValueType](https://json.nlohmann.me/api/basic_json/operator_ValueType), [get_binary](https://json.nlohmann.me/api/basic_json/get_binary) +- **Element access**: [at](https://json.nlohmann.me/api/basic_json/at), [operator[]](https://json.nlohmann.me/api/basic_json/operator[]), [value](https://json.nlohmann.me/api/basic_json/value), [front](https://json.nlohmann.me/api/basic_json/front), [back](https://json.nlohmann.me/api/basic_json/back) +- **Lookup**: [find](https://json.nlohmann.me/api/basic_json/find), [count](https://json.nlohmann.me/api/basic_json/count), [contains](https://json.nlohmann.me/api/basic_json/contains) +- **Iterators**: [begin](https://json.nlohmann.me/api/basic_json/begin), [cbegin](https://json.nlohmann.me/api/basic_json/cbegin), [end](https://json.nlohmann.me/api/basic_json/end), [cend](https://json.nlohmann.me/api/basic_json/cend), [rbegin](https://json.nlohmann.me/api/basic_json/rbegin), [rend](https://json.nlohmann.me/api/basic_json/rend), [crbegin](https://json.nlohmann.me/api/basic_json/crbegin), [crend](https://json.nlohmann.me/api/basic_json/crend), [items](https://json.nlohmann.me/api/basic_json/items) +- **Capacity**: [empty](https://json.nlohmann.me/api/basic_json/empty), [size](https://json.nlohmann.me/api/basic_json/size), [max_size](https://json.nlohmann.me/api/basic_json/max_size) +- **Modifiers**: [clear](https://json.nlohmann.me/api/basic_json/clear), [push_back](https://json.nlohmann.me/api/basic_json/push_back), [operator+=](https://json.nlohmann.me/api/basic_json/operator+=), [emplace_back](https://json.nlohmann.me/api/basic_json/emplace_back), [emplace](https://json.nlohmann.me/api/basic_json/emplace), [erase](https://json.nlohmann.me/api/basic_json/erase), [insert](https://json.nlohmann.me/api/basic_json/insert), [update](https://json.nlohmann.me/api/basic_json/update), [swap](https://json.nlohmann.me/api/basic_json/swap) +- **Lexicographical comparison operators**: [operator==](https://json.nlohmann.me/api/basic_json/operator_eq), [operator!=](https://json.nlohmann.me/api/basic_json/operator_ne), [operator<](https://json.nlohmann.me/api/basic_json/operator_lt), [operator>](https://json.nlohmann.me/api/basic_json/operator_gt), [operator<=](https://json.nlohmann.me/api/basic_json/operator_le), [operator>=](https://json.nlohmann.me/api/basic_json/operator_ge), [operator<=>](https://json.nlohmann.me/api/basic_json/operator_spaceship) +- **Serialization / Dumping**: [dump](https://json.nlohmann.me/api/basic_json/dump) +- **Deserialization / Parsing**: [parse](https://json.nlohmann.me/api/basic_json/parse), [accept](https://json.nlohmann.me/api/basic_json/accept), [sax_parse](https://json.nlohmann.me/api/basic_json/sax_parse) +- **JSON Pointer functions**: [flatten](https://json.nlohmann.me/api/basic_json/flatten), [unflatten](https://json.nlohmann.me/api/basic_json/unflatten) +- **JSON Patch functions**: [patch](https://json.nlohmann.me/api/basic_json/patch), [patch_inplace](https://json.nlohmann.me/api/basic_json/patch_inplace), [diff](https://json.nlohmann.me/api/basic_json/diff), [merge_patch](https://json.nlohmann.me/api/basic_json/merge_patch) +- **Static functions**: [meta](https://json.nlohmann.me/api/basic_json/meta), [get_allocator](https://json.nlohmann.me/api/basic_json/get_allocator) +- **Binary formats**: [from_bjdata](https://json.nlohmann.me/api/basic_json/from_bjdata), [from_bson](https://json.nlohmann.me/api/basic_json/from_bson), [from_cbor](https://json.nlohmann.me/api/basic_json/from_cbor), [from_msgpack](https://json.nlohmann.me/api/basic_json/from_msgpack), [from_ubjson](https://json.nlohmann.me/api/basic_json/from_ubjson), [to_bjdata](https://json.nlohmann.me/api/basic_json/to_bjdata), [to_bson](https://json.nlohmann.me/api/basic_json/to_bson), [to_cbor](https://json.nlohmann.me/api/basic_json/to_cbor), [to_msgpack](https://json.nlohmann.me/api/basic_json/to_msgpack), [to_ubjson](https://json.nlohmann.me/api/basic_json/to_ubjson) +- **Non-member functions**: [operator<<](https://json.nlohmann.me/api/operator_ltlt/), [operator>>](https://json.nlohmann.me/api/operator_gtgt/), [to_string](https://json.nlohmann.me/api/basic_json/to_string) +- **Literals**: [operator""_json](https://json.nlohmann.me/api/operator_literal_json) +- **Helper classes**: [std::hash<basic_json>](https://json.nlohmann.me/api/basic_json/std_hash), [std::swap<basic_json>](https://json.nlohmann.me/api/basic_json/std_swap) + +[**Full API documentation**](https://json.nlohmann.me/api/basic_json/) ## Examples Here are some examples to give you an idea how to use the class. -Beside the examples below, you may want to: +Besides the examples below, you may want to: → Check the [documentation](https://json.nlohmann.me/)\ -→ Browse the [standalone example files](https://github.com/nlohmann/json/tree/develop/docs/examples) - -Every API function (documented in the [API Documentation](https://json.nlohmann.me/api/basic_json/)) has a corresponding standalone example file. For example, the [`emplace()`](https://json.nlohmann.me/api/basic_json/emplace/) function has a matching [emplace.cpp](https://github.com/nlohmann/json/blob/develop/docs/examples/emplace.cpp) example file. +→ Browse the [standalone example files](https://github.com/nlohmann/json/tree/develop/docs/mkdocs/docs/examples)\ +→ Read the full [API Documentation](https://json.nlohmann.me/api/basic_json/) with self-contained examples for every function ### Read JSON from a file @@ -128,6 +158,19 @@ std::ifstream f("example.json"); json data = json::parse(f); ``` +If using modules (enabled with `NLOHMANN_JSON_BUILD_MODULES`), this example becomes: +```cpp +import std; +import nlohmann.json; + +using json = nlohmann::json; + +// ... + +std::ifstream f("example.json"); +json data = json::parse(f); +``` + ### Creating `json` objects from JSON literals Assume you want to create hard-code this literal JSON value in a file, as a `json` object: @@ -166,7 +209,7 @@ json ex3 = { }; ``` -### JSON as first-class data type +### JSON as a first-class data type Here are some examples to give you an idea how to use the class. @@ -195,13 +238,13 @@ With this library, you could write: // create an empty structure (null) json j; -// add a number that is stored as double (note the implicit conversion of j to an object) +// add a number stored as double (note the implicit conversion of j to an object) j["pi"] = 3.141; -// add a Boolean that is stored as bool +// add a Boolean stored as bool j["happy"] = true; -// add a string that is stored as std::string +// add a string stored as std::string j["name"] = "Niels"; // add another null object by passing nullptr @@ -210,7 +253,7 @@ j["nothing"] = nullptr; // add an object inside the object j["answer"]["everything"] = 42; -// add an array that is stored as std::vector (using an initializer list) +// add an array stored as std::vector (using an initializer list) j["list"] = { 1, 0, 2 }; // add another object (using an initializer list of pairs) @@ -302,7 +345,7 @@ Note the difference between serialization and assignment: json j_string = "this is a string"; // retrieve the string value -auto cpp_string = j_string.template get(); +auto cpp_string = j_string.get(); // retrieve the string value (alternative when a variable already exists) std::string cpp_string2; j_string.get_to(cpp_string2); @@ -311,7 +354,7 @@ j_string.get_to(cpp_string2); std::string serialized_string = j_string.dump(); // output of original string -std::cout << cpp_string << " == " << cpp_string2 << " == " << j_string.template get() << '\n'; +std::cout << cpp_string << " == " << cpp_string2 << " == " << j_string.get() << '\n'; // output of serialized value std::cout << j_string << " == " << serialized_string << std::endl; ``` @@ -320,7 +363,7 @@ std::cout << j_string << " == " << serialized_string << std::endl; 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. -#### To/from streams (e.g. files, string streams) +#### To/from streams (e.g., files, string streams) You can also use streams to serialize and deserialize: @@ -353,7 +396,7 @@ Please note that setting the exception bit for `failbit` is inappropriate for th #### Read from iterator range -You can also parse JSON from an iterator range; that is, from any container accessible by iterators whose `value_type` is an integral type of 1, 2 or 4 bytes, which will be interpreted as UTF-8, UTF-16 and UTF-32 respectively. For instance, a `std::vector`, or a `std::list`: +You can also parse JSON from an iterator range; that is, from any container accessible by iterators whose `value_type` is an integral type of 1, 2, or 4 bytes, which will be interpreted as UTF-8, UTF-16, and UTF-32 respectively. For instance, a `std::vector`, or a `std::list`: ```cpp std::vector v = {'t', 'r', 'u', 'e'}; @@ -457,7 +500,7 @@ To implement your own SAX handler, proceed as follows: 2. Create an object of your SAX interface class, e.g. `my_sax`. 3. Call `bool json::sax_parse(input, &my_sax)`; where the first parameter can be any input like a string or an input stream and the second parameter is a pointer to your SAX interface. -Note the `sax_parse` function only returns a `bool` indicating the result of the last executed SAX event. It does not return a `json` value - it is up to you to decide what to do with the SAX events. Furthermore, no exceptions are thrown in case of a parse error - it is up to you what to do with the exception object passed to your `parse_error` implementation. Internally, the SAX interface is used for the DOM parser (class `json_sax_dom_parser`) as well as the acceptor (`json_sax_acceptor`), see file [`json_sax.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/json_sax.hpp). +Note the `sax_parse` function only returns a `bool` indicating the result of the last executed SAX event. It does not return a `json` value - it is up to you to decide what to do with the SAX events. Furthermore, no exceptions are thrown in case of a parse error -- it is up to you what to do with the exception object passed to your `parse_error` implementation. Internally, the SAX interface is used for the DOM parser (class `json_sax_dom_parser`) as well as the acceptor (`json_sax_acceptor`), see file [`json_sax.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/json_sax.hpp). ### STL-like access @@ -484,7 +527,7 @@ for (auto& element : j) { } // getter/setter -const auto tmp = j[0].template get(); +const auto tmp = j[0].get(); j[1] = 42; bool foo = j.at(2); @@ -547,7 +590,6 @@ int fob_present = o.count("fob"); // 0 o.erase("foo"); ``` - ### Conversion from STL containers Any sequence container (`std::array`, `std::vector`, `std::deque`, `std::forward_list`, `std::list`) whose values can be used to construct JSON values (e.g., integers, floating point numbers, Booleans, string types, or again STL containers described in this section) can be used to create a JSON array. The same holds for similar associative containers (`std::set`, `std::multiset`, `std::unordered_set`, `std::unordered_multiset`), but in these cases the order of the elements of the array depends on how the elements are ordered in the respective STL container. @@ -590,7 +632,7 @@ json j_umset(c_umset); // both entries for "one" are used // maybe ["one", "two", "one", "four"] ``` -Likewise, any associative key-value containers (`std::map`, `std::multimap`, `std::unordered_map`, `std::unordered_multimap`) whose keys can construct an `std::string` and whose values can be used to construct JSON values (see examples above) can be used to create a JSON object. Note that in case of multimaps only one key is used in the JSON object and the value depends on the internal order of the STL container. +Likewise, any associative key-value containers (`std::map`, `std::multimap`, `std::unordered_map`, `std::unordered_multimap`) whose keys can construct an `std::string` and whose values can be used to construct JSON values (see examples above) can be used to create a JSON object. Note that in case of multimaps, only one key is used in the JSON object and the value depends on the internal order of the STL container. ```cpp std::map c_map { {"one", 1}, {"two", 2}, {"three", 3} }; @@ -612,7 +654,7 @@ json j_ummap(c_ummap); // only one entry for key "three" is used ### JSON Pointer and JSON Patch -The library supports **JSON Pointer** ([RFC 6901](https://tools.ietf.org/html/rfc6901)) as alternative means to address structured values. On top of this, **JSON Patch** ([RFC 6902](https://tools.ietf.org/html/rfc6902)) allows describing differences between two JSON values - effectively allowing patch and diff operations known from Unix. +The library supports **JSON Pointer** ([RFC 6901](https://tools.ietf.org/html/rfc6901)) as an alternative means to address structured values. On top of this, **JSON Patch** ([RFC 6902](https://tools.ietf.org/html/rfc6902)) allows describing differences between two JSON values -- effectively allowing patch and diff operations known from Unix. ```cpp // a JSON value @@ -692,7 +734,7 @@ You can switch off implicit conversions by defining `JSON_USE_IMPLICIT_CONVERSIO // strings std::string s1 = "Hello, world!"; json js = s1; -auto s2 = js.template get(); +auto s2 = js.get(); // NOT RECOMMENDED std::string s3 = js; std::string s4; @@ -701,7 +743,7 @@ s4 = js; // Booleans bool b1 = true; json jb = b1; -auto b2 = jb.template get(); +auto b2 = jb.get(); // NOT RECOMMENDED bool b3 = jb; bool b4; @@ -710,7 +752,7 @@ b4 = jb; // numbers int i = 42; json jn = i; -auto f = jn.template get(); +auto f = jn.get(); // NOT RECOMMENDED double f2 = jb; double f3; @@ -753,9 +795,9 @@ j["age"] = p.age; // convert from JSON: copy each value from the JSON object ns::person p { - j["name"].template get(), - j["address"].template get(), - j["age"].template get() + j["name"].get(), + j["address"].get(), + j["age"].get() }; ``` @@ -772,7 +814,7 @@ std::cout << j << std::endl; // {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"} // conversion: json -> person -auto p2 = j.template get(); +auto p2 = j.get(); // that's it assert(p == p2); @@ -799,30 +841,25 @@ namespace ns { ``` That's all! When calling the `json` constructor with your type, your custom `to_json` method will be automatically called. -Likewise, when calling `template get()` or `get_to(your_type&)`, the `from_json` method will be called. +Likewise, when calling `get()` or `get_to(your_type&)`, the `from_json` method will be called. Some important things: -* Those methods **MUST** be in your type's namespace (which can be the global namespace), or the library will not be able to locate them (in this example, they are in namespace `ns`, where `person` is defined). -* Those methods **MUST** be available (e.g., proper headers must be included) everywhere you use these conversions. Look at [issue 1108](https://github.com/nlohmann/json/issues/1108) for errors that may occur otherwise. -* When using `template get()`, `your_type` **MUST** be [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). (There is a way to bypass this requirement described later.) -* In function `from_json`, use function [`at()`](https://json.nlohmann.me/api/basic_json/at/) to access the object values rather than `operator[]`. In case a key does not exist, `at` throws an exception that you can handle, whereas `operator[]` exhibits undefined behavior. -* You do not need to add serializers or deserializers for STL types like `std::vector`: the library already implements these. +- Those methods **MUST** be in your type's namespace (which can be the global namespace), or the library will not be able to locate them (in this example, they are in namespace `ns`, where `person` is defined). +- Those methods **MUST** be available (e.g., proper headers must be included) everywhere you use these conversions. Look at [issue 1108](https://github.com/nlohmann/json/issues/1108) for errors that may occur otherwise. +- When using `get()`, `your_type` **MUST** be [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). (There is a way to bypass this requirement described later.) +- In function `from_json`, use function [`at()`](https://json.nlohmann.me/api/basic_json/at/) to access the object values rather than `operator[]`. In case a key does not exist, `at` throws an exception that you can handle, whereas `operator[]` exhibits undefined behavior. +- You do not need to add serializers or deserializers for STL types like `std::vector`: the library already implements these. #### Simplify your life with macros -If you just want to serialize/deserialize some structs, the `to_json`/`from_json` functions can be a lot of boilerplate. +If you just want to serialize/deserialize some structs, the `to_json`/`from_json` functions can be a lot of boilerplate. There are [**several macros**](https://json.nlohmann.me/features/arbitrary_types/#simplify-your-life-with-macros) to make your life easier as long as you (1) want to use a JSON object as serialization and (2) want to use the member variable names as object keys in that object. -There are two macros to make your life easier as long as you (1) want to use a JSON object as serialization and (2) want to use the member variable names as object keys in that object: +Which macro to choose depends on whether private member variables need to be accessed, a deserialization is needed, missing values should yield an error or should be replaced by default values, and if derived classes are used. See [this overview to choose the right one for your use case](https://json.nlohmann.me/api/macros/#serializationdeserialization-macros). -- `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(name, member1, member2, ...)` is to be defined inside the namespace of the class/struct to create code for. -- `NLOHMANN_DEFINE_TYPE_INTRUSIVE(name, member1, member2, ...)` is to be defined inside the class/struct to create code for. This macro can also access private members. +##### Example usage of macros -In both macros, the first parameter is the name of the class/struct, and all remaining parameters name the members. - -##### Examples - -The `to_json`/`from_json` functions for the `person` struct above can be created with: +The `to_json`/`from_json` functions for the `person` struct above can be created with [`NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`](https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/). In all macros, the first parameter is the name of the class/struct, and all remaining parameters name the members. ```cpp namespace ns { @@ -830,7 +867,7 @@ namespace ns { } ``` -Here is an example with private members, where `NLOHMANN_DEFINE_TYPE_INTRUSIVE` is needed: +Here is another example with private members, where [`NLOHMANN_DEFINE_TYPE_INTRUSIVE`](https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/) is needed: ```cpp namespace ns { @@ -839,7 +876,7 @@ namespace ns { std::string street; int housenumber; int postcode; - + public: NLOHMANN_DEFINE_TYPE_INTRUSIVE(address, street, housenumber, postcode) }; @@ -850,7 +887,7 @@ namespace ns { This requires a bit more advanced technique. But first, let's see how this conversion mechanism works: -The library uses **JSON Serializers** to convert types to json. +The library uses **JSON Serializers** to convert types to JSON. The default serializer for `nlohmann::json` is `nlohmann::adl_serializer` (ADL means [Argument-Dependent Lookup](https://en.cppreference.com/w/cpp/language/adl)). It is implemented like this (simplified): @@ -890,8 +927,8 @@ namespace nlohmann { if (j.is_null()) { opt = boost::none; } else { - opt = j.template get(); // same as above, but with - // adl_serializer::from_json + opt = j.get(); // same as above, but with + // adl_serializer::from_json } } }; @@ -900,7 +937,7 @@ namespace nlohmann { #### How can I use `get()` for non-default constructible/non-copyable types? -There is a way, if your type is [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible). You will need to specialize the `adl_serializer` as well, but with a special `from_json` overload: +There is a way if your type is [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible). You will need to specialize the `adl_serializer` as well, but with a special `from_json` overload: ```cpp struct move_only_type { @@ -918,7 +955,7 @@ namespace nlohmann { // note: the return type is no longer 'void', and the method only takes // one argument static move_only_type from_json(const json& j) { - return {j.template get()}; + return {j.get()}; } // Here's the catch! You must provide a to_json method! Otherwise, you @@ -973,23 +1010,23 @@ struct bad_serializer { template static void to_json(BasicJsonType& j, const T& value) { - // this calls BasicJsonType::json_serializer::to_json(j, value); + // this calls BasicJsonType::json_serializer::to_json(j, value) // if BasicJsonType::json_serializer == bad_serializer ... oops! j = value; } template static void to_json(const BasicJsonType& j, T& value) { - // this calls BasicJsonType::json_serializer::from_json(j, value); + // this calls BasicJsonType::json_serializer::from_json(j, value) // if BasicJsonType::json_serializer == bad_serializer ... oops! - value = j.template get(); // oops! + value = j.get(); // oops! } }; ``` ### Specializing enum conversion -By default, enum values are serialized to JSON as integers. In some cases this could result in undesired behavior. If an enum is modified or re-ordered after data has been serialized to JSON, the later de-serialized JSON data may be undefined or a different enum value than was originally intended. +By default, enum values are serialized to JSON as integers. In some cases, this could result in undesired behavior. If an enum is modified or re-ordered after data has been serialized to JSON, the later deserialized JSON data may be undefined or a different enum value than was originally intended. It is possible to more precisely specify how a given enum is mapped to and from JSON as shown below: @@ -1022,19 +1059,21 @@ assert(j == "stopped"); // json string to enum json j3 = "running"; -assert(j3.template get() == TS_RUNNING); +assert(j3.get() == TS_RUNNING); // undefined json value to enum (where the first map entry above is the default) json jPi = 3.14; -assert(jPi.template get() == TS_INVALID ); +assert(jPi.get() == TS_INVALID); ``` Just as in [Arbitrary Type Conversions](#arbitrary-types-conversions) above, + - `NLOHMANN_JSON_SERIALIZE_ENUM()` MUST be declared in your enum type's namespace (which can be the global namespace), or the library will not be able to locate it, and it will default to integer serialization. - It MUST be available (e.g., proper headers must be included) everywhere you use the conversions. Other Important points: -- When using `template get()`, undefined JSON values will default to the first pair specified in your map. Select this default pair carefully. + +- When using `get()`, undefined JSON values will default to the first pair specified in your map. Select this default pair carefully. - If an enum or JSON value is specified more than once in your map, the first matching occurrence from the top of the map will be returned when converting to or from JSON. ### Binary formats (BSON, CBOR, MessagePack, UBJSON, and BJData) @@ -1108,14 +1147,19 @@ binary.set_subtype(0x10); auto cbor = json::to_msgpack(j); // 0xD5 (fixext2), 0x10, 0xCA, 0xFE ``` +## Customers + +The library is used in multiple projects, applications, operating systems, etc. The list below is not exhaustive, but the result of an internet search. If you know further customers of the library, please let me know, see [contact](#contact). + +[![logos of customers using the library](docs/mkdocs/docs/images/customers.png)](https://json.nlohmann.me/home/customers/) ## Supported compilers -Though it's 2023 already, the support for C++11 is still a bit sparse. Currently, the following compilers are known to work: +Though it's 2026 already, the support for C++11 is still a bit sparse. Currently, the following compilers are known to work: -- GCC 4.8 - 12.0 (and possibly later) -- Clang 3.4 - 15.0 (and possibly later) -- Apple Clang 9.1 - 13.1 (and possibly later) +- GCC 4.8 - 14.2 (and possibly later) +- Clang 3.4 - 21.0 (and possibly later) +- Apple Clang 9.1 - 16.0 (and possibly later) - Intel C++ Compiler 17.0.2 (and possibly later) - Nvidia CUDA Compiler 11.0.221 (and possibly later) - Microsoft Visual C++ 2015 / Build Tools 14.0.25123.0 (and possibly later) @@ -1127,10 +1171,10 @@ I would be happy to learn about other compilers/versions. Please note: -- GCC 4.8 has a bug [57824](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57824)): multiline raw strings cannot be the arguments to macros. Don't use multiline raw strings directly in macros with this compiler. +- GCC 4.8 has a bug [57824](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57824): multiline raw strings cannot be the arguments to macros. Don't use multiline raw strings directly in macros with this compiler. - Android defaults to using very old compilers and C++ libraries. To fix this, add the following to your `Application.mk`. This will switch to the LLVM C++ library, the Clang compiler, and enable C++11 and other features disabled by default. - ``` + ```makefile APP_STL := c++_shared NDK_TOOLCHAIN_VERSION := clang3.6 APP_CPPFLAGS += -frtti -fexceptions @@ -1138,72 +1182,11 @@ Please note: The code compiles successfully with [Android NDK](https://developer.android.com/ndk/index.html?hl=ml), Revision 9 - 11 (and possibly later) and [CrystaX's Android NDK](https://www.crystax.net/en/android/ndk) version 10. -- For GCC running on MinGW or Android SDK, the error `'to_string' is not a member of 'std'` (or similarly, for `strtod` or `strtof`) may occur. Note this is not an issue with the code, but rather with the compiler itself. On Android, see above to build with a newer environment. For MinGW, please refer to [this site](https://tehsausage.com/mingw-to-string) and [this discussion](https://github.com/nlohmann/json/issues/136) for information on how to fix this bug. For Android NDK using `APP_STL := gnustl_static`, please refer to [this discussion](https://github.com/nlohmann/json/issues/219). +- For GCC running on MinGW or Android SDK, the error `'to_string' is not a member of 'std'` (or similarly, for `strtod` or `strtof`) may occur. Note this is not an issue with the code, but rather with the compiler itself. On Android, see above to build with a newer environment. For MinGW, please refer to [this site](https://tehsausage.com/mingw-to-string) and [this discussion](https://github.com/nlohmann/json/issues/136) for information on how to fix this bug. For Android NDK using `APP_STL := gnustl_static`, please refer to [this discussion](https://github.com/nlohmann/json/issues/219). - Unsupported versions of GCC and Clang are rejected by `#error` directives. This can be switched off by defining `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK`. Note that you can expect no support in this case. -The following compilers are currently used in continuous integration at [AppVeyor](https://ci.appveyor.com/project/nlohmann/json), [Cirrus CI](https://cirrus-ci.com/github/nlohmann/json), and [GitHub Actions](https://github.com/nlohmann/json/actions): - -| Compiler | Operating System | CI Provider | -|--------------------------------------------------------------------------------------------------------|--------------------|----------------| -| Apple Clang 11.0.3 (clang-1103.0.32.62); Xcode 11.7 | macOS 11.7.1 | GitHub Actions | -| Apple Clang 12.0.0 (clang-1200.0.32.29); Xcode 12.4 | macOS 11.7.1 | GitHub Actions | -| Apple Clang 12.0.5 (clang-1205.0.22.11); Xcode 12.5.1 | macOS 11.7.1 | GitHub Actions | -| Apple Clang 13.0.0 (clang-1300.0.29.3); Xcode 13.0 | macOS 11.7.1 | GitHub Actions | -| Apple Clang 13.0.0 (clang-1300.0.29.3); Xcode 13.1 | macOS 12.6.1 | GitHub Actions | -| Apple Clang 13.0.0 (clang-1300.0.29.30); Xcode 13.2.1 | macOS 12.6.1 | GitHub Actions | -| Apple Clang 13.1.6 (clang-1316.0.21.2.3); Xcode 13.3.1 | macOS 12.6.1 | GitHub Actions | -| Apple Clang 13.1.6 (clang-1316.0.21.2.5); Xcode 13.4.1 | macOS 12.6.1 | GitHub Actions | -| Apple Clang 14.0.0 (clang-1400.0.29.102); Xcode 14.0 | macOS 12.6.1 | GitHub Actions | -| Apple Clang 14.0.0 (clang-1400.0.29.102); Xcode 14.0.1 | macOS 12.6.1 | GitHub Actions | -| Apple Clang 14.0.0 (clang-1400.0.29.202); Xcode 14.1 | macOS 12.6.1 | GitHub Actions | -| Clang 3.5.2 | Ubuntu 20.04.3 LTS | GitHub Actions | -| Clang 3.6.2 | Ubuntu 20.04.3 LTS | GitHub Actions | -| Clang 3.7.1 | Ubuntu 20.04.3 LTS | GitHub Actions | -| Clang 3.8.1 | Ubuntu 20.04.3 LTS | GitHub Actions | -| Clang 3.9.1 | Ubuntu 20.04.3 LTS | GitHub Actions | -| Clang 4.0.1 | Ubuntu 20.04.3 LTS | GitHub Actions | -| Clang 5.0.2 | Ubuntu 20.04.3 LTS | GitHub Actions | -| Clang 6.0.1 | Ubuntu 20.04.3 LTS | GitHub Actions | -| Clang 7.0.1 | Ubuntu 20.04.3 LTS | GitHub Actions | -| Clang 8.0.0 | Ubuntu 20.04.3 LTS | GitHub Actions | -| Clang 9.0.0 | Ubuntu 20.04.3 LTS | GitHub Actions | -| Clang 10.0.0 | Ubuntu 20.04.3 LTS | GitHub Actions | -| Clang 10.0.0 with GNU-like command-line | Windows-10.0.17763 | GitHub Actions | -| Clang 11.0.0 with GNU-like command-line | Windows-10.0.17763 | GitHub Actions | -| Clang 11.0.0 with MSVC-like command-line | Windows-10.0.17763 | GitHub Actions | -| Clang 11.0.0 | Ubuntu 20.04.3 LTS | GitHub Actions | -| Clang 12.0.0 | Ubuntu 20.04.3 LTS | GitHub Actions | -| Clang 12.0.0 with GNU-like command-line | Windows-10.0.17763 | GitHub Actions | -| Clang 13.0.0 | Ubuntu 20.04.3 LTS | GitHub Actions | -| Clang 13.0.0 with GNU-like command-line | Windows-10.0.17763 | GitHub Actions | -| Clang 14.0.0 | Ubuntu 20.04.3 LTS | GitHub Actions | -| Clang 14.0.0 with GNU-like command-line | Windows-10.0.17763 | GitHub Actions | -| Clang 15.0.0 with GNU-like command-line | Windows-10.0.17763 | GitHub Actions | -| Clang 15.0.4 | Ubuntu 20.04.3 LTS | GitHub Actions | -| Clang 16.0.0 (16.0.0-++20221031071727+500876226c60-1~exp1~20221031071831.439) | Ubuntu 20.04.3 LTS | GitHub Actions | -| GCC 4.8.5 (Ubuntu 4.8.5-4ubuntu2) | Ubuntu 20.04.3 LTS | GitHub Actions | -| GCC 4.9.4 | Ubuntu 20.04.3 LTS | GitHub Actions | -| GCC 5.5.0 | Ubuntu 20.04.3 LTS | GitHub Actions | -| GCC 6.5.0 | Ubuntu 20.04.3 LTS | GitHub Actions | -| GCC 7.5.0 | Ubuntu 20.04.3 LTS | GitHub Actions | -| GCC 8.1.0 (i686-posix-dwarf-rev0, Built by MinGW-W64 project) | Windows-10.0.17763 | GitHub Actions | -| GCC 8.1.0 (x86_64-posix-seh-rev0, Built by MinGW-W64 project) | Windows-10.0.17763 | GitHub Actions | -| GCC 8.5.0 | Ubuntu 20.04.3 LTS | GitHub Actions | -| GCC 9.5.0 | Ubuntu 20.04.3 LTS | GitHub Actions | -| GCC 10.4.0 | Ubuntu 20.04.3 LTS | GitHub Actions | -| GCC 11.1.0 | Ubuntu (aarch64) | Cirrus CI | -| GCC 11.3.0 | Ubuntu 20.04.3 LTS | GitHub Actions | -| GCC 12.2.0 | Ubuntu 20.04.3 LTS | GitHub Actions | -| GCC 13.0.0 20220605 (experimental) | Ubuntu 20.04.3 LTS | GitHub Actions | -| Intel C++ Compiler 2021.5.0.20211109 | Ubuntu 20.04.3 LTS | GitHub Actions | -| NVCC 11.0.221 | Ubuntu 20.04.3 LTS | GitHub Actions | -| Visual Studio 14 2015 MSVC 19.0.24241.7 (Build Engine version 14.0.25420.1) | Windows-6.3.9600 | AppVeyor | -| Visual Studio 15 2017 MSVC 19.16.27035.0 (Build Engine version 15.9.21+g9802d43bc3 for .NET Framework) | Windows-10.0.14393 | AppVeyor | -| Visual Studio 16 2019 MSVC 19.28.29912.0 (Build Engine version 16.9.0+57a23d249 for .NET Framework) | Windows-10.0.17763 | GitHub Actions | -| Visual Studio 16 2019 MSVC 19.28.29912.0 (Build Engine version 16.9.0+57a23d249 for .NET Framework) | Windows-10.0.17763 | AppVeyor | -| Visual Studio 17 2022 MSVC 19.30.30709.0 (Build Engine version 17.0.31804.368 for .NET Framework) | Windows-10.0.20348 | GitHub Actions | - +See the page [quality assurance](https://json.nlohmann.me/community/quality_assurance) on the compilers used to check the library in the CI. ## Integration @@ -1218,7 +1201,7 @@ using json = nlohmann::json; to the files you want to process JSON and set the necessary switches to enable C++11 (e.g., `-std=c++11` for GCC and Clang). -You can further use file [`include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/json_fwd.hpp) for forward-declarations. The installation of json_fwd.hpp (as part of cmake's install step), can be achieved by setting `-DJSON_MultipleHeaders=ON`. +You can further use file [`include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/json_fwd.hpp) for forward-declarations. The installation of `json_fwd.hpp` (as part of cmake's install step) can be achieved by setting `-DJSON_MultipleHeaders=ON`. ### CMake @@ -1230,7 +1213,7 @@ To use this library from a CMake project, you can locate it directly with `find_ ```cmake # CMakeLists.txt -find_package(nlohmann_json 3.2.0 REQUIRED) +find_package(nlohmann_json 3.12.0 REQUIRED) ... add_library(foo ...) ... @@ -1270,16 +1253,17 @@ Since CMake v3.11, be used to automatically download a release as a dependency at configure time. Example: + ```cmake include(FetchContent) -FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz) +FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.12.0/json.tar.xz) FetchContent_MakeAvailable(json) target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json) ``` -**Note**: It is recommended to use the URL approach described above which is supported as of version 3.10.0. See +**Note**: It is recommended to use the URL approach described above, which is supported as of version 3.10.0. See for more information. #### Supporting Both @@ -1300,11 +1284,12 @@ add_library(foo ...) # import method target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json) ``` + ```cmake # thirdparty/CMakeLists.txt ... if(FOO_USE_EXTERNAL_JSON) - find_package(nlohmann_json 3.2.0 REQUIRED) + find_package(nlohmann_json 3.12.0 REQUIRED) else() set(JSON_BuildTests OFF CACHE INTERNAL "") add_subdirectory(nlohmann_json) @@ -1316,51 +1301,24 @@ endif() ### Package Managers -:beer: If you are using OS X and [Homebrew](https://brew.sh), just type `brew install nlohmann-json` and you're set. If you want the bleeding edge rather than the latest release, use `brew install nlohmann-json --HEAD`. See [nlohmann-json](https://formulae.brew.sh/formula/nlohmann-json) for more information. - -If you are using the [Meson Build System](https://mesonbuild.com), add this source tree as a [meson subproject](https://mesonbuild.com/Subprojects.html#using-a-subproject). You may also use the `include.zip` published in this project's [Releases](https://github.com/nlohmann/json/releases) to reduce the size of the vendored source tree. Alternatively, you can get a wrap file by downloading it from [Meson WrapDB](https://wrapdb.mesonbuild.com/nlohmann_json), or simply use `meson wrap install nlohmann_json`. Please see the meson project for any issues regarding the packaging. - -The provided `meson.build` can also be used as an alternative to CMake for installing `nlohmann_json` system-wide in which case a pkg-config file is installed. To use it, simply have your build system require the `nlohmann_json` pkg-config dependency. In Meson, it is preferred to use the [`dependency()`](https://mesonbuild.com/Reference-manual.html#dependency) object with a subproject fallback, rather than using the subproject directly. - -If you are using [Bazel](https://bazel.build/) you can simply reference this repository using `http_archive` or `git_repository` and depend on `@nlohmann_json//:json`. - -If you are using [Conan](https://www.conan.io/) to manage your dependencies, merely add [`nlohmann_json/x.y.z`](https://conan.io/center/nlohmann_json) to your `conanfile`'s requires, where `x.y.z` is the release version you want to use. Please file issues [here](https://github.com/conan-io/conan-center-index/issues) if you experience problems with the packages. - -If you are using [Spack](https://www.spack.io/) to manage your dependencies, you can use the [`nlohmann-json` package](https://spack.readthedocs.io/en/latest/package_list.html#nlohmann-json). Please see the [spack project](https://github.com/spack/spack) for any issues regarding the packaging. - -If you are using [hunter](https://github.com/cpp-pm/hunter) on your project for external dependencies, then you can use the [nlohmann_json package](https://hunter.readthedocs.io/en/latest/packages/pkg/nlohmann_json.html). Please see the hunter project for any issues regarding the packaging. - -If you are using [Buckaroo](https://buckaroo.pm), you can install this library's module with `buckaroo add github.com/buckaroo-pm/nlohmann-json`. Please file issues [here](https://github.com/buckaroo-pm/nlohmann-json). There is a demo repo [here](https://github.com/njlr/buckaroo-nholmann-json-example). - -If you are using [vcpkg](https://github.com/Microsoft/vcpkg/) on your project for external dependencies, then you can install the [nlohmann-json package](https://github.com/Microsoft/vcpkg/tree/master/ports/nlohmann-json) with `vcpkg install nlohmann-json` and follow the then displayed descriptions. Please see the vcpkg project for any issues regarding the packaging. - -If you are using [cget](https://cget.readthedocs.io/en/latest/), you can install the latest development version with `cget install nlohmann/json`. A specific version can be installed with `cget install nlohmann/json@v3.1.0`. Also, the multiple header version can be installed by adding the `-DJSON_MultipleHeaders=ON` flag (i.e., `cget install nlohmann/json -DJSON_MultipleHeaders=ON`). - -If you are using [CocoaPods](https://cocoapods.org), you can use the library by adding pod `"nlohmann_json", '~>3.1.2'` to your podfile (see [an example](https://bitbucket.org/benman/nlohmann_json-cocoapod/src/master/)). Please file issues [here](https://bitbucket.org/benman/nlohmann_json-cocoapod/issues?status=new&status=open). - -If you are using [Swift Package Manager](https://swift.org/package-manager/), you can use the library by adding a package dependency to this repository. And target dependency as `.product(name: "nlohmann-json", package: "json")`. - -If you are using [NuGet](https://www.nuget.org), you can use the package [nlohmann.json](https://www.nuget.org/packages/nlohmann.json/). Please check [this extensive description](https://github.com/nlohmann/json/issues/1132#issuecomment-452250255) on how to use the package. Please file issues [here](https://github.com/hnkb/nlohmann-json-nuget/issues). - -If you are using [conda](https://conda.io/), you can use the package [nlohmann_json](https://github.com/conda-forge/nlohmann_json-feedstock) from [conda-forge](https://conda-forge.org) executing `conda install -c conda-forge nlohmann_json`. Please file issues [here](https://github.com/conda-forge/nlohmann_json-feedstock/issues). - -If you are using [MSYS2](https://www.msys2.org/), you can use the [mingw-w64-nlohmann-json](https://packages.msys2.org/base/mingw-w64-nlohmann-json) package, just type `pacman -S mingw-w64-i686-nlohmann-json` or `pacman -S mingw-w64-x86_64-nlohmann-json` for installation. Please file issues [here](https://github.com/msys2/MINGW-packages/issues/new?title=%5Bnlohmann-json%5D) if you experience problems with the packages. - -If you are using [MacPorts](https://ports.macports.org), execute `sudo port install nlohmann-json` to install the [nlohmann-json](https://ports.macports.org/port/nlohmann-json/) package. - -If you are using [`build2`](https://build2.org), you can use the [`nlohmann-json`](https://cppget.org/nlohmann-json) package from the public repository https://cppget.org or directly from the [package's sources repository](https://github.com/build2-packaging/nlohmann-json). In your project's `manifest` file, just add `depends: nlohmann-json` (probably with some [version constraints](https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml#guide-add-remove-deps)). If you are not familiar with using dependencies in `build2`, [please read this introduction](https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml). -Please file issues [here](https://github.com/build2-packaging/nlohmann-json) if you experience problems with the packages. - -If you are using [`wsjcpp`](https://wsjcpp.org), you can use the command `wsjcpp install "https://github.com/nlohmann/json:develop"` to get the latest version. Note you can change the branch ":develop" to an existing tag or another branch. - -If you are using [`CPM.cmake`](https://github.com/TheLartians/CPM.cmake), you can check this [`example`](https://github.com/TheLartians/CPM.cmake/tree/master/examples/json). After [adding CPM script](https://github.com/TheLartians/CPM.cmake#adding-cpm) to your project, implement the following snippet to your CMake: - -```cmake -CPMAddPackage( - NAME nlohmann_json - GITHUB_REPOSITORY nlohmann/json - VERSION 3.9.1) -``` +Use your favorite [**package manager**](https://json.nlohmann.me/integration/package_managers/) to use the library. + +-  [**Homebrew**](https://json.nlohmann.me/integration/package_managers/#homebrew) `nlohmann-json` +-  [**Meson**](https://json.nlohmann.me/integration/package_managers/#meson) `nlohmann_json` +-  [**Bazel**](https://json.nlohmann.me/integration/package_managers/#bazel) `nlohmann_json` +-  [**Conan**](https://json.nlohmann.me/integration/package_managers/#conan) `nlohmann_json` +-  [**Spack**](https://json.nlohmann.me/integration/package_managers/#spack) `nlohmann-json` +- [**Hunter**](https://json.nlohmann.me/integration/package_managers/#hunter) `nlohmann_json` +-  [**vcpkg**](https://json.nlohmann.me/integration/package_managers/#vcpkg) `nlohmann-json` +- [**cget**](https://json.nlohmann.me/integration/package_managers/#cget) `nlohmann/json` +-  [**Swift Package Manager**](https://json.nlohmann.me/integration/package_managers/#swift-package-manager) `nlohmann/json` +-  [**Nuget**](https://json.nlohmann.me/integration/package_managers/#nuget) `nlohmann.json` +-  [**Conda**](https://json.nlohmann.me/integration/package_managers/#conda) `nlohmann_json` +-  [**MacPorts**](https://json.nlohmann.me/integration/package_managers/#macports) `nlohmann-json` +-  [**cpm.cmake**](https://json.nlohmann.me/integration/package_managers/#cpmcmake) `gh:nlohmann/json` +-  [**xmake**](https://json.nlohmann.me/integration/package_managers/#xmake) `nlohmann_json` + +The library is part of many package managers. See the [**documentation**](https://json.nlohmann.me/integration/package_managers/) for detailed descriptions and examples. ### Pkg-config @@ -1370,20 +1328,13 @@ If you are using bare Makefiles, you can use `pkg-config` to generate the includ pkg-config nlohmann_json --cflags ``` -Users of the Meson build system will also be able to use a system-wide library, which will be found by `pkg-config`: - -```meson -json = dependency('nlohmann_json', required: true) -``` - - ## License - +OSI approved license The class is licensed under the [MIT License](https://opensource.org/licenses/MIT): -Copyright © 2013-2022 [Niels Lohmann](https://nlohmann.me) +Copyright © 2013-2026 [Niels Lohmann](https://nlohmann.me) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: @@ -1393,13 +1344,19 @@ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR I * * * -The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2008-2009 [Björn Hoehrmann](https://bjoern.hoehrmann.de/) +- The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2008-2009 [Björn Hoehrmann](https://bjoern.hoehrmann.de/) +- The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2009 [Florian Loitsch](https://florian.loitsch.com/) +- The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/). +- The class contains parts of [Google Abseil](https://github.com/abseil/abseil-cpp) which is licensed under the [Apache 2.0 License](https://opensource.org/licenses/Apache-2.0). -The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2009 [Florian Loitsch](https://florian.loitsch.com/) +REUSE Software -The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/). +The library is compliant to version 3.3 of the [**REUSE specification**](https://reuse.software): -The class contains parts of [Google Abseil](https://github.com/abseil/abseil-cpp) which is licensed under the [Apache 2.0 License](https://opensource.org/licenses/Apache-2.0). +- Every source file contains an SPDX copyright header. +- The full text of all licenses used in the repository can be found in the `LICENSES` folder. +- File `.reuse/dep5` contains an overview of all files' copyrights and licenses. +- Run `pipx run reuse lint` to verify the project's REUSE compliance and `pipx run reuse spdx` to generate a SPDX SBOM. ## Contact @@ -1415,7 +1372,7 @@ Only if your request would contain confidential information, please [send me an I deeply appreciate the help of the following people. - +GitHub avatars of the contributors 1. [Teemperor](https://github.com/Teemperor) implemented CMake support and lcov integration, realized escape and Unicode handling in the string parser, and fixed the JSON serialization. 2. [elliotgoodrich](https://github.com/elliotgoodrich) fixed an issue with double deletion in the iterator classes. @@ -1428,13 +1385,13 @@ I deeply appreciate the help of the following people. 9. [Florian Weber](https://github.com/Florianjw) fixed a bug in and improved the performance of the comparison operators. 10. [Eric Cornelius](https://github.com/EricMCornelius) pointed out a bug in the handling with NaN and infinity values. He also improved the performance of the string escaping. 11. [易思龙](https://github.com/likebeta) implemented a conversion from anonymous enums. -12. [kepkin](https://github.com/kepkin) patiently pushed forward the support for Microsoft Visual studio. +12. [kepkin](https://github.com/kepkin) patiently pushed forward the support for Microsoft Visual Studio. 13. [gregmarr](https://github.com/gregmarr) simplified the implementation of reverse iterators and helped with numerous hints and improvements. In particular, he pushed forward the implementation of user-defined types. 14. [Caio Luppi](https://github.com/caiovlp) fixed a bug in the Unicode handling. 15. [dariomt](https://github.com/dariomt) fixed some typos in the examples. 16. [Daniel Frey](https://github.com/d-frey) cleaned up some pointers and implemented exception-safe memory allocation. 17. [Colin Hirsch](https://github.com/ColinH) took care of a small namespace issue. -18. [Huu Nguyen](https://github.com/whoshuu) correct a variable name in the documentation. +18. [Huu Nguyen](https://github.com/whoshuu) corrected a variable name in the documentation. 19. [Silverweed](https://github.com/silverweed) overloaded `parse()` to accept an rvalue reference. 20. [dariomt](https://github.com/dariomt) fixed a subtlety in MSVC type support and implemented the `get_ref()` function to get a reference to stored values. 21. [ZahlGraf](https://github.com/ZahlGraf) added a workaround that allows compilation using Android NDK. @@ -1469,7 +1426,7 @@ I deeply appreciate the help of the following people. 50. [Jared Grubb](https://github.com/jaredgrubb) silenced a nasty documentation warning. 51. [Yixin Zhang](https://github.com/qwename) fixed an integer overflow check. 52. [Bosswestfalen](https://github.com/Bosswestfalen) merged two iterator classes into a smaller one. -53. [Daniel599](https://github.com/Daniel599) helped to get Travis execute the tests with Clang's sanitizers. +53. [Daniel599](https://github.com/Daniel599) helped to get Travis to execute the tests with Clang's sanitizers. 54. [Jonathan Lee](https://github.com/vjon) fixed an example in the README file. 55. [gnzlbg](https://github.com/gnzlbg) supported the implementation of user-defined types. 56. [Alexej Harm](https://github.com/qis) helped to get the user-defined types working with Visual Studio. @@ -1490,7 +1447,7 @@ I deeply appreciate the help of the following people. 71. [Vincent Thiery](https://github.com/vthiery) maintains a package for the Conan package manager. 72. [Steffen](https://github.com/koemeet) fixed a potential issue with MSVC and `std::min`. 73. [Mike Tzou](https://github.com/Chocobo1) fixed some typos. -74. [amrcode](https://github.com/amrcode) noted a misleading documentation about comparison of floats. +74. [amrcode](https://github.com/amrcode) noted misleading documentation about comparison of floats. 75. [Oleg Endo](https://github.com/olegendo) reduced the memory consumption by replacing `` with ``. 76. [dan-42](https://github.com/dan-42) cleaned up the CMake files to simplify including/reusing of the library. 77. [Nikita Ofitserov](https://github.com/himikof) allowed for moving values from initializer lists. @@ -1517,13 +1474,13 @@ I deeply appreciate the help of the following people. 98. [Vadim Evard](https://github.com/Pipeliner) fixed a Markdown issue in the README. 99. [zerodefect](https://github.com/zerodefect) fixed a compiler warning. 100. [Kert](https://github.com/kaidokert) allowed to template the string type in the serialization and added the possibility to override the exceptional behavior. -101. [mark-99](https://github.com/mark-99) helped fixing an ICC error. +101. [mark-99](https://github.com/mark-99) helped fix an ICC error. 102. [Patrik Huber](https://github.com/patrikhuber) fixed links in the README file. 103. [johnfb](https://github.com/johnfb) found a bug in the implementation of CBOR's indefinite length strings. 104. [Paul Fultz II](https://github.com/pfultz2) added a note on the cget package manager. 105. [Wilson Lin](https://github.com/wla80) made the integration section of the README more concise. 106. [RalfBielig](https://github.com/ralfbielig) detected and fixed a memory leak in the parser callback. -107. [agrianius](https://github.com/agrianius) allowed to dump JSON to an alternative string type. +107. [agrianius](https://github.com/agrianius) allowed dumping JSON to an alternative string type. 108. [Kevin Tonon](https://github.com/ktonon) overworked the C++11 compiler checks in CMake. 109. [Axel Huebl](https://github.com/ax3l) simplified a CMake check and added support for the [Spack package manager](https://spack.io). 110. [Carlos O'Ryan](https://github.com/coryan) fixed a typo. @@ -1534,7 +1491,7 @@ I deeply appreciate the help of the following people. 115. [Matthias Möller](https://github.com/TinyTinni) removed the dependency from `std::stringstream`. 116. [agrianius](https://github.com/agrianius) added code to use alternative string implementations. 117. [Daniel599](https://github.com/Daniel599) allowed to use more algorithms with the `items()` function. -118. [Julius Rakow](https://github.com/jrakow) fixed the Meson include directory and fixed the links to [cppreference.com](cppreference.com). +118. [Julius Rakow](https://github.com/jrakow) fixed the Meson include directory and fixed the links to [cppreference.com](https://cppreference.com). 119. [Sonu Lohani](https://github.com/sonulohani) fixed the compilation with MSVC 2015 in debug mode. 120. [grembo](https://github.com/grembo) fixed the test suite and re-enabled several test cases. 121. [Hyeon Kim](https://github.com/simnalamburt) introduced the macro `JSON_INTERNAL_CATCH` to control the exception handling inside the library. @@ -1572,12 +1529,12 @@ I deeply appreciate the help of the following people. 153. [Ivor Wanders](https://github.com/iwanders) helped to reduce the CMake requirement to version 3.1. 154. [njlr](https://github.com/njlr) updated the Buckaroo instructions. 155. [Lion](https://github.com/lieff) fixed a compilation issue with GCC 7 on CentOS. -156. [Isaac Nickaein](https://github.com/nickaein) improved the integer serialization performance and implemented the `contains()` function. +156. [Isaac Nickaein](https://github.com/nickaein) improved the integer serialization performance and implemented the `contains()` function. 157. [past-due](https://github.com/past-due) suppressed an unfixable warning. 158. [Elvis Oric](https://github.com/elvisoric) improved Meson support. 159. [Matěj Plch](https://github.com/Afforix) fixed an example in the README. 160. [Mark Beckwith](https://github.com/wythe) fixed a typo. -161. [scinart](https://github.com/scinart) fixed bug in the serializer. +161. [scinart](https://github.com/scinart) fixed a bug in the serializer. 162. [Patrick Boettcher](https://github.com/pboettch) implemented `push_back()` and `pop_back()` for JSON Pointers. 163. [Bruno Oliveira](https://github.com/nicoddemus) added support for Conda. 164. [Michele Caini](https://github.com/skypjack) fixed links in the README. @@ -1619,7 +1576,7 @@ I deeply appreciate the help of the following people. 200. [Alexander “weej” Jones](https://github.com/alex-weej) fixed an example in the README. 201. [Antoine Cœur](https://github.com/Coeur) fixed some typos in the documentation. 202. [jothepro](https://github.com/jothepro) updated links to the Hunter package. -203. [Dave Lee](https://github.com/kastiglione) fixed link in the README. +203. [Dave Lee](https://github.com/kastiglione) fixed a link in the README. 204. [Joël Lamotte](https://github.com/Klaim) added instruction for using Build2's package manager. 205. [Paul Jurczak](https://github.com/pauljurczak) fixed an example in the README. 206. [Sonu Lohani](https://github.com/sonulohani) fixed a warning. @@ -1662,7 +1619,7 @@ I deeply appreciate the help of the following people. 243. [raduteo](https://github.com/raduteo) fixed a warning. 244. [David Pfahler](https://github.com/theShmoo) added the possibility to compile the library without I/O support. 245. [Morten Fyhn Amundsen](https://github.com/mortenfyhn) fixed a typo. -246. [jpl-mac](https://github.com/jpl-mac) allowed to treat the library as a system header in CMake. +246. [jpl-mac](https://github.com/jpl-mac) allowed treating the library as a system header in CMake. 247. [Jason Dsouza](https://github.com/jasmcaus) fixed the indentation of the CMake file. 248. [offa](https://github.com/offa) added a link to Conan Center to the documentation. 249. [TotalCaesar659](https://github.com/TotalCaesar659) updated the links in the documentation to use HTTPS. @@ -1704,7 +1661,7 @@ I deeply appreciate the help of the following people. 285. [Wolf Vollprecht](https://github.com/wolfv) added the `patch_inplace` function. 286. [Jake Zimmerman](https://github.com/jez) highlighted common usage patterns in the README file. 287. [NN](https://github.com/NN---) added the Visual Studio output directory to `.gitignore`. -288. [Romain Reignier](https://github.com/romainreignier) improved the performance the vector output adapter. +288. [Romain Reignier](https://github.com/romainreignier) improved the performance of the vector output adapter. 289. [Mike](https://github.com/Mike-Leo-Smith) fixed the `std::iterator_traits`. 290. [Richard Hozák](https://github.com/zxey) added macro `JSON_NO_ENUM` to disable default enum conversions. 291. [vakokako](https://github.com/vakokako) fixed tests when compiling with C++20. @@ -1751,9 +1708,9 @@ I deeply appreciate the help of the following people. 332. [taro](https://github.com/tarolling) fixed a typo in the `CODEOWNERS` file. 333. [Ikko Eltociear Ashimine](https://github.com/eltociear) fixed a typo. 334. [Felix Yan](https://github.com/felixonmars) fixed a typo in the README. -335. [HO-COOH](https://github.com/HO-COOH) fixed a parentheses in the documentation. +335. [HO-COOH](https://github.com/HO-COOH) fixed a parenthesis in the documentation. 336. [Ivor Wanders](https://github.com/iwanders) fixed the examples to catch exception by `const&`. -337. [miny1233](https://github.com/miny1233) fixed a parentheses in the documentation. +337. [miny1233](https://github.com/miny1233) fixed a parenthesis in the documentation. 338. [tomalakgeretkal](https://github.com/tomalakgeretkal) fixed a compilation error. 339. [alferov](https://github.com/ALF-ONE) fixed a compilation error. 340. [Craig Scott](https://github.com/craigscott-crascit) fixed a deprecation warning in CMake. @@ -1763,10 +1720,47 @@ I deeply appreciate the help of the following people. 344. [Aleksei Sapitskii](https://github.com/aleksproger) added support for Apple's Swift Package Manager. 345. [Benjamin Buch](https://github.com/bebuch) fixed the installation path in CMake. 346. [Colby Haskell](https://github.com/colbychaskell) clarified the parse error message in case a file cannot be opened. +347. [Juan Carlos Arevalo Baeza](https://github.com/TheJCAB) fixed the enum conversion. +348. [alferov](https://github.com/ALF-ONE) fixed a version in the documentation. +349. [ss](https://github.com/serge-s) fixed the amalgamation call. +350. [AniketDhemare](https://github.com/AniketDhemare) fixed a version in the documentation. +351. [Philip Müller](https://github.com/philip-paul-mueller) fixed an example. +352. [Leila Shcheglova](https://github.com/LeilaShcheglova) fixed a warning in a test. +353. [Alex Prabhat Bara](https://github.com/alexprabhat99) fixed a function name in the documentation. +354. [laterlaugh](https://github.com/laterlaugh) fixed some typos. +355. [Yuanhao Jia](https://github.com/MrJia1997) fixed the GDB pretty printer. +356. [Fallen_Breath](https://github.com/Fallen-Breath) fixed an example for JSON Pointer. +357. [Nikhil Idiculla](https://github.com/tsnl) fixed some typos. +358. [Griffin Myers](https://github.com/gmyers18) updated the Natvis file. +359. [thetimr](https://github.com/thetimr) fixed a typo in the documentation. +360. [Balazs Erseki](https://github.com/zerocukor287) fixed a URL in the contribution guidelines. +361. [Niccolò Iardella](https://github.com/rotolof) added `NLOHMANN_DEFINE_DERIVED_TYPE_*` macros. +362. [Borislav Stanimirov](https://github.com/iboB) allowed overriding the CMake target name. +363. [Captain Crutches](https://github.com/captaincrutches) made `iterator_proxy_value` a `std::forward_iterator`. +364. [Fredrik Sandhei](https://github.com/fsandhei) added type conversion support for `std::optional`. +365. [jh96](https://github.com/jordan-hoang) added exceptions when `nullptr` is passed to `parse`. +366. [Stuart Gorman](https://github.com/StuartGorman) fixed number parsing when `EINTR` set in `errno`. +367. [Dylan Baker](https://github.com/dcbaker) generated a pkg-config file that follows the pkg-config conventions. +368. [Tianyi Chen](https://github.com/TianyiChen) optimized the binary `get_number` implementation. +369. [peng-wang-cn](https://github.com/peng-wang-cn) added type conversion support for multidimensional arrays. +370. [Einars Netlis-Galejs](https://github.com/EinarsNG) added `ONLY_SERIALIZE` for `NLOHMANN_DEFINE_DERIVED_TYPE_*` macros. +371. [Marcel](https://github.com/mering) removed `alwayslink=True` Bazel flag. +372. [Harinath Nampally](https://github.com/hnampally) added diagnostic positions to exceptions. +373. [Nissim Armand Ben Danan](https://github.com/NissimBendanan) fixed `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT` with an empty JSON instance. +374. [Michael Valladolid](https://github.com/codenut) added support for BSON uint64 serialization/deserialization. +375. [Nikhil](https://github.com/nikhilreddydev) updated the documentation. +376. [Nebojša Cvetković](https://github.com/nebkat) added support for BJDATA optimized binary array type. +377. [Sushrut Shringarputale](https://github.com/sushshring) added support for diagnostic positions. +378. [kimci86](https://github.com/kimci86) templated to `NLOHMANN_DEFINE_TYPE` macros to also support `ordered_json`. +379. [Richard Topchii](https://github.com/richardtop) added support for VisionOS in the Swift Package Manager. +380. [Robert Chisholm](https://github.com/Robadob) fixed a typo. +381. [zjyhjqs](https://github.com/zjyhjqs) added CPack support. +382. [bitFiedler](https://github.com/bitFiedler) made GDB pretty printer work with Python 3.8. +383. [Gianfranco Costamagna](https://github.com/LocutusOfBorg) fixed a compiler warning. +384. [risa2000](https://github.com/risa2000) made `std::filesystem::path` conversion to/from UTF-8 encoded string explicit. Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone. - ## Used third-party tools The library itself consists of a single header file licensed under the MIT license. However, it is built, tested, documented, and whatnot using a lot of third-party tools and services. Thanks a lot! @@ -1777,16 +1771,15 @@ The library itself consists of a single header file licensed under the MIT licen - [**Artistic Style**](http://astyle.sourceforge.net) for automatic source code indentation - [**Clang**](https://clang.llvm.org) for compilation with code sanitizers - [**CMake**](https://cmake.org) for build automation -- [**Codacy**](https://www.codacy.com) for further [code analysis](https://www.codacy.com/app/nlohmann/json) +- [**Codacy**](https://www.codacy.com) for further [code analysis](https://app.codacy.com/gh/nlohmann/json/dashboard) - [**Coveralls**](https://coveralls.io) to measure [code coverage](https://coveralls.io/github/nlohmann/json) - [**Coverity Scan**](https://scan.coverity.com) for [static analysis](https://scan.coverity.com/projects/nlohmann-json) - [**cppcheck**](http://cppcheck.sourceforge.net) for static analysis - [**doctest**](https://github.com/onqtam/doctest) for the unit tests -- [**git-update-ghpages**](https://github.com/rstacruz/git-update-ghpages) to upload the documentation to gh-pages - [**GitHub Changelog Generator**](https://github.com/skywinder/github-changelog-generator) to generate the [ChangeLog](https://github.com/nlohmann/json/blob/develop/ChangeLog.md) - [**Google Benchmark**](https://github.com/google/benchmark) to implement the benchmarks - [**Hedley**](https://nemequ.github.io/hedley/) to avoid re-inventing several compiler-agnostic feature macros -- [**lcov**](http://ltp.sourceforge.net/coverage/lcov.php) to process coverage information and create an HTML view +- [**lcov**](https://github.com/linux-test-project/lcov) to process coverage information and create an HTML view - [**libFuzzer**](https://llvm.org/docs/LibFuzzer.html) to implement fuzz testing for OSS-Fuzz - [**Material for MkDocs**](https://squidfunk.github.io/mkdocs-material/) for the style of the documentation site - [**MkDocs**](https://www.mkdocs.org) for the documentation site @@ -1794,19 +1787,13 @@ The library itself consists of a single header file licensed under the MIT licen - [**Probot**](https://probot.github.io) for automating maintainer tasks such as closing stale issues, requesting missing information, or detecting toxic comments. - [**Valgrind**](https://valgrind.org) to check for correct memory management - -## Projects using JSON for Modern C++ - -The library is currently used in Apple macOS Sierra-Monterey and iOS 10-15. I am not sure what they are using the library for, but I am happy that it runs on so many devices. - - ## Notes ### Character encoding The library supports **Unicode input** as follows: -- Only **UTF-8** encoded input is supported which is the default encoding for JSON according to [RFC 8259](https://tools.ietf.org/html/rfc8259.html#section-8.1). +- Only **UTF-8** encoded input is supported, which is the default encoding for JSON according to [RFC 8259](https://tools.ietf.org/html/rfc8259.html#section-8.1). - `std::u16string` and `std::u32string` can be parsed, assuming UTF-16 and UTF-32 encoding, respectively. These encodings are not supported when reading from files or other input containers. - Other encodings such as Latin-1 or ISO 8859-1 are **not** supported and will yield parse or serialization errors. - [Unicode noncharacters](https://www.unicode.org/faq/private_use.html#nonchar1) will not be replaced by the library. @@ -1821,14 +1808,24 @@ This library does not support comments by default. It does so for three reasons: 1. Comments are not part of the [JSON specification](https://tools.ietf.org/html/rfc8259). You may argue that `//` or `/* */` are allowed in JavaScript, but JSON is not JavaScript. 2. This was not an oversight: Douglas Crockford [wrote on this](https://plus.google.com/118095276221607585885/posts/RK8qyGVaGSr) in May 2012: + + > I removed comments from JSON because I saw people were using them to hold parsing directives, a practice which would have destroyed interoperability. I know that the lack of comments makes some people sad, but it shouldn't. + > + > Suppose you are using JSON to keep configuration files, which you would like to annotate. Go ahead and insert all the comments you like. Then pipe it through JSMin before handing it to your JSON parser. + +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. - > I removed comments from JSON because I saw people were using them to hold parsing directives, a practice which would have destroyed interoperability. I know that the lack of comments makes some people sad, but it shouldn't. +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. - > Suppose you are using JSON to keep configuration files, which you would like to annotate. Go ahead and insert all the comments you like. Then pipe it through JSMin before handing it to your JSON parser. +### Trailing commas -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. +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. -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. +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 @@ -1836,6 +1833,8 @@ By default, the library does not preserve the **insertion order of object elemen If you do want to preserve the insertion order, you can try the type [`nlohmann::ordered_json`](https://github.com/nlohmann/json/issues/2179). Alternatively, you can use a more sophisticated ordered map like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) ([integration](https://github.com/nlohmann/json/issues/546#issuecomment-304447518)) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) ([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)). +See the [**documentation on object order**](https://json.nlohmann.me/features/object_order/) for more information. + ### Memory Release We checked with Valgrind and the Address Sanitizer (ASAN) that there are no memory leaks. @@ -1847,26 +1846,26 @@ Here is a related issue [#1924](https://github.com/nlohmann/json/issues/1924). ### Further notes -- The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](https://en.cppreference.com/w/cpp/error/assert). In particular, note [`operator[]`](https://json.nlohmann.me/api/basic_json/operator%5B%5D/) implements **unchecked access** for const objects: If the given key is not present, the behavior is undefined (think of a dereferenced null pointer) and yields an [assertion failure](https://github.com/nlohmann/json/issues/289) if assertions are switched on. If you are not sure whether an element in an object exists, use checked access with the [`at()` function](https://json.nlohmann.me/api/basic_json/at/). Furthermore, you can define `JSON_ASSERT(x)` to replace calls to `assert(x)`. +- The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](https://en.cppreference.com/w/cpp/error/assert). In particular, note [`operator[]`](https://json.nlohmann.me/api/basic_json/operator%5B%5D/) implements **unchecked access** for const objects: If the given key is not present, the behavior is undefined (think of a dereferenced null pointer) and yields an [assertion failure](https://github.com/nlohmann/json/issues/289) if assertions are switched on. If you are not sure whether an element in an object exists, use checked access with the [`at()` function](https://json.nlohmann.me/api/basic_json/at/). Furthermore, you can define `JSON_ASSERT(x)` to replace calls to `assert(x)`. See the [**documentation on runtime assertions**](https://json.nlohmann.me/features/assertions/) for more information. - As the exact number type is not defined in the [JSON specification](https://tools.ietf.org/html/rfc8259.html), this library tries to choose the best fitting C++ number type automatically. As a result, the type `double` may be used to store numbers which may yield [**floating-point exceptions**](https://github.com/nlohmann/json/issues/181) in certain rare situations if floating-point exceptions have been unmasked in the calling code. These exceptions are not caused by the library and need to be fixed in the calling code, such as by re-masking the exceptions prior to calling library functions. - The code can be compiled without C++ **runtime type identification** features; that is, you can use the `-fno-rtti` compiler flag. -- **Exceptions** are used widely within the library. They can, however, be switched off with either using the compiler flag `-fno-exceptions` or by defining the symbol `JSON_NOEXCEPTION`. In this case, exceptions are replaced by `abort()` calls. You can further control this behavior by defining `JSON_THROW_USER` (overriding `throw`), `JSON_TRY_USER` (overriding `try`), and `JSON_CATCH_USER` (overriding `catch`). Note that `JSON_THROW_USER` should leave the current scope (e.g., by throwing or aborting), as continuing after it may yield undefined behavior. Note the explanatory [`what()`](https://en.cppreference.com/w/cpp/error/exception/what) string of exceptions is not available for MSVC if exceptions are disabled, see [#2824](https://github.com/nlohmann/json/discussions/2824). +- **Exceptions** are used widely within the library. They can, however, be switched off with either using the compiler flag `-fno-exceptions` or by defining the symbol `JSON_NOEXCEPTION`. In this case, exceptions are replaced by `abort()` calls. You can further control this behavior by defining `JSON_THROW_USER` (overriding `throw`), `JSON_TRY_USER` (overriding `try`), and `JSON_CATCH_USER` (overriding `catch`). Note that `JSON_THROW_USER` should leave the current scope (e.g., by throwing or aborting), as continuing after it may yield undefined behavior. Note the explanatory [`what()`](https://en.cppreference.com/w/cpp/error/exception/what) string of exceptions is not available for MSVC if exceptions are disabled, see [#2824](https://github.com/nlohmann/json/discussions/2824). See the [**documentation of exceptions**](https://json.nlohmann.me/home/exceptions/) for more information. ## Execute unit tests To compile and run the tests, you need to execute -```sh -$ mkdir build -$ cd build -$ cmake .. -DJSON_BuildTests=On -$ cmake --build . -$ ctest --output-on-failure +```shell +mkdir build +cd build +cmake .. -DJSON_BuildTests=On +cmake --build . +ctest --output-on-failure ``` Note that during the `ctest` stage, several JSON test files are downloaded from an [external repository](https://github.com/nlohmann/json_test_data). If policies forbid downloading artifacts during testing, you can download the files yourself and pass the directory with the test files via `-DJSON_TestDataDirectory=path` to CMake. Then, no Internet connectivity is required. See [issue #2189](https://github.com/nlohmann/json/issues/2189) for more information. -If the test suite is not found, several test suites will fail like this: +If the testdata is not found, several test suites will fail like this: ``` =============================================================================== @@ -1884,8 +1883,10 @@ json/tests/src/make_test_data_available.hpp:23: FATAL ERROR: REQUIRE( utils::che In case you have downloaded the library rather than checked out the code via Git, test `cmake_fetch_content_configure` will fail. Please execute `ctest -LE git_required` to skip these tests. See [issue #2189](https://github.com/nlohmann/json/issues/2189) for more information. -Some tests change the installed files and hence make the whole process not reproducible. Please execute `ctest -LE not_reproducible` to skip these tests. See [issue #2324](https://github.com/nlohmann/json/issues/2324) for more information. +Some tests are requiring network to be properly execute. They are labeled as `git_required`. Please execute `ctest -LE git_required` to skip these tests. See [issue #4851](https://github.com/nlohmann/json/issues/4851) for more information. + +Some tests change the installed files and hence make the whole process not reproducible. Please execute `ctest -LE not_reproducible` to skip these tests. See [issue #2324](https://github.com/nlohmann/json/issues/2324) for more information. Furthermore, assertions must be switched off to ensure reproducible builds (see [discussion 4494](https://github.com/nlohmann/json/discussions/4494)). Note you need to call `cmake -LE "not_reproducible|git_required"` to exclude both labels. See [issue #2596](https://github.com/nlohmann/json/issues/2596) for more information. -As Intel compilers use unsafe floating point optimization by default, the unit tests may fail. Use flag [`/fp:precise`](https://software.intel.com/content/www/us/en/develop/documentation/cpp-compiler-developer-guide-and-reference/top/compiler-reference/compiler-options/compiler-option-details/floating-point-options/fp-model-fp.html) then. +As Intel compilers use unsafe floating point optimization by default, the unit tests may fail. Use flag [`/fp:precise`](https://www.intel.com/content/www/us/en/docs/cpp-compiler/developer-guide-reference/2021-8/fp-model-fp.html) then. diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel deleted file mode 100644 index 2b2ae9dba7..0000000000 --- a/WORKSPACE.bazel +++ /dev/null @@ -1 +0,0 @@ -workspace(name = "nlohmann_json") diff --git a/cmake/ci.cmake b/cmake/ci.cmake index bbb2d4cb95..f711368649 100644 --- a/cmake/ci.cmake +++ b/cmake/ci.cmake @@ -8,34 +8,24 @@ set(N 10) include(FindPython3) find_package(Python3 COMPONENTS Interpreter) -find_program(ASTYLE_TOOL NAMES astyle) -execute_process(COMMAND ${ASTYLE_TOOL} --version OUTPUT_VARIABLE ASTYLE_TOOL_VERSION ERROR_VARIABLE ASTYLE_TOOL_VERSION) -string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" ASTYLE_TOOL_VERSION "${ASTYLE_TOOL_VERSION}") -message(STATUS "🔖 Artistic Style ${ASTYLE_TOOL_VERSION} (${ASTYLE_TOOL})") - -find_program(CLANG_TOOL NAMES clang++-HEAD clang++ clang++-17 clang++-16 clang++-15 clang++-14 clang++-13 clang++-12 clang++-11 clang++) +find_program(CLANG_TOOL NAMES clang++-HEAD clang++ clang++-20 clang++-19 clang++-18 clang++-17 clang++-16 clang++-15 clang++-14 clang++-13 clang++-12 clang++-11 clang++) execute_process(COMMAND ${CLANG_TOOL} --version OUTPUT_VARIABLE CLANG_TOOL_VERSION ERROR_VARIABLE CLANG_TOOL_VERSION) string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" CLANG_TOOL_VERSION "${CLANG_TOOL_VERSION}") message(STATUS "🔖 Clang ${CLANG_TOOL_VERSION} (${CLANG_TOOL})") -find_program(CLANG_TIDY_TOOL NAMES clang-tidy-17 clang-tidy-16 clang-tidy-15 clang-tidy-14 clang-tidy-13 clang-tidy-12 clang-tidy-11 clang-tidy) +find_program(CLANG_TIDY_TOOL NAMES clang-tidy-20 clang-tidy-19 clang-tidy-18 clang-tidy-17 clang-tidy-16 clang-tidy-15 clang-tidy-14 clang-tidy-13 clang-tidy-12 clang-tidy-11 clang-tidy) execute_process(COMMAND ${CLANG_TIDY_TOOL} --version OUTPUT_VARIABLE CLANG_TIDY_TOOL_VERSION ERROR_VARIABLE CLANG_TIDY_TOOL_VERSION) string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" CLANG_TIDY_TOOL_VERSION "${CLANG_TIDY_TOOL_VERSION}") message(STATUS "🔖 Clang-Tidy ${CLANG_TIDY_TOOL_VERSION} (${CLANG_TIDY_TOOL})") message(STATUS "🔖 CMake ${CMAKE_VERSION} (${CMAKE_COMMAND})") -find_program(CPPCHECK_TOOL NAMES cppcheck) -execute_process(COMMAND ${CPPCHECK_TOOL} --version OUTPUT_VARIABLE CPPCHECK_TOOL_VERSION ERROR_VARIABLE CPPCHECK_TOOL_VERSION) -string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" CPPCHECK_TOOL_VERSION "${CPPCHECK_TOOL_VERSION}") -message(STATUS "🔖 Cppcheck ${CPPCHECK_TOOL_VERSION} (${CPPCHECK_TOOL})") - -find_program(GCC_TOOL NAMES g++-latest g++-HEAD g++-13 g++-12 g++-11 g++-10) +find_program(GCC_TOOL NAMES g++-latest g++-HEAD g++ g++-15 g++-14 g++-13 g++-12 g++-11 g++-10) execute_process(COMMAND ${GCC_TOOL} --version OUTPUT_VARIABLE GCC_TOOL_VERSION ERROR_VARIABLE GCC_TOOL_VERSION) string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" GCC_TOOL_VERSION "${GCC_TOOL_VERSION}") message(STATUS "🔖 GCC ${GCC_TOOL_VERSION} (${GCC_TOOL})") -find_program(GCOV_TOOL NAMES gcov-HEAD gcov-11 gcov-10 gcov) +find_program(GCOV_TOOL NAMES gcov-HEAD gcov gcov-15 gcov-14 gcov-13 gcov-12 gcov-11 gcov-10) execute_process(COMMAND ${GCOV_TOOL} --version OUTPUT_VARIABLE GCOV_TOOL_VERSION ERROR_VARIABLE GCOV_TOOL_VERSION) string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" GCOV_TOOL_VERSION "${GCOV_TOOL_VERSION}") message(STATUS "🔖 GCOV ${GCOV_TOOL_VERSION} (${GCOV_TOOL})") @@ -88,332 +78,8 @@ file(GLOB_RECURSE SRC_FILES ${PROJECT_SOURCE_DIR}/include/nlohmann/*.hpp) # Thorough check with recent compilers ############################################################################### -# Ignored Clang warnings: -# -Wno-c++98-compat The library targets C++11. -# -Wno-c++98-compat-pedantic The library targets C++11. -# -Wno-deprecated-declarations The library contains annotations for deprecated functions. -# -Wno-extra-semi-stmt The library uses std::assert which triggers this warning. -# -Wno-padded We do not care about padding warnings. -# -Wno-covered-switch-default All switches list all cases and a default case. -# -Wno-unsafe-buffer-usage Otherwise Doctest would not compile. -# -Wno-weak-vtables The library is header-only. -# -Wreserved-identifier See https://github.com/onqtam/doctest/issues/536. - -set(CLANG_CXXFLAGS - -Werror - -Weverything - -Wno-c++98-compat - -Wno-c++98-compat-pedantic - -Wno-deprecated-declarations - -Wno-extra-semi-stmt - -Wno-padded - -Wno-covered-switch-default - -Wno-unsafe-buffer-usage - -Wno-weak-vtables - -Wno-reserved-identifier -) - -# Warning flags determined for GCC 13.0 (experimental) with https://github.com/nlohmann/gcc_flags: -# Ignored GCC warnings: -# -Wno-abi-tag We do not care about ABI tags. -# -Wno-aggregate-return The library uses aggregate returns. -# -Wno-long-long The library uses the long long type to interface with system functions. -# -Wno-namespaces The library uses namespaces. -# -Wno-padded We do not care about padding warnings. -# -Wno-system-headers We do not care about warnings in system headers. -# -Wno-templates The library uses templates. - -set(GCC_CXXFLAGS - -pedantic - -Werror - --all-warnings - --extra-warnings - -W - -WNSObject-attribute - -Wno-abi-tag - -Waddress - -Waddress-of-packed-member - -Wno-aggregate-return - -Waggressive-loop-optimizations - -Waligned-new=all - -Wall - -Walloc-zero - -Walloca - -Wanalyzer-double-fclose - -Wanalyzer-double-free - -Wanalyzer-exposure-through-output-file - -Wanalyzer-file-leak - -Wanalyzer-free-of-non-heap - -Wanalyzer-malloc-leak - -Wanalyzer-mismatching-deallocation - -Wanalyzer-null-argument - -Wanalyzer-null-dereference - -Wanalyzer-possible-null-argument - -Wanalyzer-possible-null-dereference - -Wanalyzer-shift-count-negative - -Wanalyzer-shift-count-overflow - -Wanalyzer-stale-setjmp-buffer - -Wanalyzer-tainted-allocation-size - -Wanalyzer-tainted-array-index - -Wanalyzer-tainted-divisor - -Wanalyzer-tainted-offset - -Wanalyzer-tainted-size - -Wanalyzer-too-complex - -Wanalyzer-unsafe-call-within-signal-handler - -Wanalyzer-use-after-free - -Wanalyzer-use-of-pointer-in-stale-stack-frame - -Wanalyzer-use-of-uninitialized-value - -Wanalyzer-va-arg-type-mismatch - -Wanalyzer-va-list-exhausted - -Wanalyzer-va-list-leak - -Wanalyzer-va-list-use-after-va-end - -Wanalyzer-write-to-const - -Wanalyzer-write-to-string-literal - -Warith-conversion - -Warray-bounds=2 - -Warray-compare - -Warray-parameter=2 - -Wattribute-alias=2 - -Wattribute-warning - -Wattributes - -Wbool-compare - -Wbool-operation - -Wbuiltin-declaration-mismatch - -Wbuiltin-macro-redefined - -Wc++0x-compat - -Wc++11-compat - -Wc++11-extensions - -Wc++14-compat - -Wc++14-extensions - -Wc++17-compat - -Wc++17-extensions - -Wc++1z-compat - -Wc++20-compat - -Wc++20-extensions - -Wc++23-extensions - -Wc++2a-compat - -Wcannot-profile - -Wcast-align - -Wcast-align=strict - -Wcast-function-type - -Wcast-qual - -Wcatch-value=3 - -Wchar-subscripts - -Wclass-conversion - -Wclass-memaccess - -Wclobbered - -Wcomma-subscript - -Wcomment - -Wcomments - -Wconditionally-supported - -Wconversion - -Wconversion-null - -Wcoverage-invalid-line-number - -Wcoverage-mismatch - -Wcpp - -Wctad-maybe-unsupported - -Wctor-dtor-privacy - -Wdangling-else - -Wdangling-pointer=2 - -Wdate-time - -Wdelete-incomplete - -Wdelete-non-virtual-dtor - -Wdeprecated - -Wdeprecated-copy - -Wdeprecated-copy-dtor - -Wdeprecated-declarations - -Wdeprecated-enum-enum-conversion - -Wdeprecated-enum-float-conversion - -Wdisabled-optimization - -Wdiv-by-zero - -Wdouble-promotion - -Wduplicated-branches - -Wduplicated-cond - -Weffc++ - -Wempty-body - -Wendif-labels - -Wenum-compare - -Wenum-conversion - -Wexceptions - -Wexpansion-to-defined - -Wextra - -Wextra-semi - -Wfloat-conversion - -Wfloat-equal - -Wformat-diag - -Wformat-overflow=2 - -Wformat-signedness - -Wformat-truncation=2 - -Wformat=2 - -Wframe-address - -Wfree-nonheap-object - -Whsa - -Wif-not-aligned - -Wignored-attributes - -Wignored-qualifiers - -Wimplicit-fallthrough=5 - -Winaccessible-base - -Winfinite-recursion - -Winherited-variadic-ctor - -Winit-list-lifetime - -Winit-self - -Winline - -Wint-in-bool-context - -Wint-to-pointer-cast - -Winterference-size - -Winvalid-imported-macros - -Winvalid-memory-model - -Winvalid-offsetof - -Winvalid-pch - -Wliteral-suffix - -Wlogical-not-parentheses - -Wlogical-op - -Wno-long-long - -Wlto-type-mismatch - -Wmain - -Wmaybe-uninitialized - -Wmemset-elt-size - -Wmemset-transposed-args - -Wmisleading-indentation - -Wmismatched-dealloc - -Wmismatched-new-delete - -Wmismatched-tags - -Wmissing-attributes - -Wmissing-braces - -Wmissing-declarations - -Wmissing-field-initializers - -Wmissing-include-dirs - -Wmissing-profile - -Wmissing-requires - -Wmissing-template-keyword - -Wmultichar - -Wmultiple-inheritance - -Wmultistatement-macros - -Wno-namespaces - -Wnarrowing - -Wnoexcept - -Wnoexcept-type - -Wnon-template-friend - -Wnon-virtual-dtor - -Wnonnull - -Wnonnull-compare - -Wnormalized=nfkc - -Wnull-dereference - -Wodr - -Wold-style-cast - -Wopenacc-parallelism - -Wopenmp-simd - -Woverflow - -Woverlength-strings - -Woverloaded-virtual - -Wpacked - -Wpacked-bitfield-compat - -Wpacked-not-aligned - -Wno-padded - -Wparentheses - -Wpedantic - -Wpessimizing-move - -Wplacement-new=2 - -Wpmf-conversions - -Wpointer-arith - -Wpointer-compare - -Wpragmas - -Wprio-ctor-dtor - -Wpsabi - -Wrange-loop-construct - -Wredundant-decls - -Wredundant-move - -Wredundant-tags - -Wregister - -Wreorder - -Wrestrict - -Wreturn-local-addr - -Wreturn-type - -Wscalar-storage-order - -Wsequence-point - -Wshadow=compatible-local - -Wshadow=global - -Wshadow=local - -Wshift-count-negative - -Wshift-count-overflow - -Wshift-negative-value - -Wshift-overflow=2 - -Wsign-compare - -Wsign-conversion - -Wsign-promo - -Wsized-deallocation - -Wsizeof-array-argument - -Wsizeof-array-div - -Wsizeof-pointer-div - -Wsizeof-pointer-memaccess - -Wstack-protector - -Wstrict-aliasing=3 - -Wstrict-null-sentinel - -Wno-strict-overflow - -Wstring-compare - -Wstringop-overflow=4 - -Wstringop-overread - -Wstringop-truncation - -Wsubobject-linkage - -Wsuggest-attribute=cold - -Wsuggest-attribute=const - -Wsuggest-attribute=format - -Wsuggest-attribute=malloc - -Wsuggest-attribute=noreturn - -Wsuggest-attribute=pure - -Wsuggest-final-methods - -Wsuggest-final-types - -Wsuggest-override - -Wswitch - -Wswitch-bool - -Wswitch-default - -Wswitch-enum - -Wswitch-outside-range - -Wswitch-unreachable - -Wsync-nand - -Wsynth - -Wno-system-headers - -Wtautological-compare - -Wno-templates - -Wterminate - -Wtrampolines - -Wtrigraphs - -Wtrivial-auto-var-init - -Wtsan - -Wtype-limits - -Wundef - -Wuninitialized - -Wunknown-pragmas - -Wunreachable-code - -Wunsafe-loop-optimizations - -Wunused - -Wunused-but-set-parameter - -Wunused-but-set-variable - -Wunused-const-variable=2 - -Wunused-function - -Wunused-label - -Wunused-local-typedefs - -Wunused-macros - -Wunused-parameter - -Wunused-result - -Wunused-value - -Wunused-variable - -Wuse-after-free=3 - -Wuseless-cast - -Wvarargs - -Wvariadic-macros - -Wvector-operation-performance - -Wvexing-parse - -Wvirtual-inheritance - -Wvirtual-move-assign - -Wvla - -Wvla-parameter - -Wvolatile - -Wvolatile-register-var - -Wwrite-strings - -Wzero-as-null-pointer-constant - -Wzero-length-bounds -) +include(clang_flags) +include(gcc_flags) add_custom_target(ci_test_gcc COMMAND CXX=${GCC_TOOL} CXXFLAGS="${GCC_CXXFLAGS}" ${CMAKE_COMMAND} @@ -439,7 +105,7 @@ add_custom_target(ci_test_clang # Different C++ Standards. ############################################################################### -foreach(CXX_STANDARD 11 14 17 20 23) +foreach(CXX_STANDARD 11 14 17 20 23 26) add_custom_target(ci_test_gcc_cxx${CXX_STANDARD} COMMAND CXX=${GCC_TOOL} CXXFLAGS="${GCC_CXXFLAGS}" ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug -GNinja @@ -461,6 +127,19 @@ foreach(CXX_STANDARD 11 14 17 20 23) COMMAND cd ${PROJECT_BINARY_DIR}/build_clang_cxx${CXX_STANDARD} && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure COMMENT "Compile and test with Clang for C++${CXX_STANDARD}" ) + + add_custom_target(ci_test_clang_libcxx_cxx${CXX_STANDARD} + COMMAND CXX=${CLANG_TOOL} CXXFLAGS="${CLANG_CXXFLAGS}" ${CMAKE_COMMAND} + -DCMAKE_BUILD_TYPE=Debug -GNinja + -DJSON_BuildTests=ON -DJSON_FastTests=ON + -DJSON_TestStandards=${CXX_STANDARD} + -DCMAKE_CXX_FLAGS="-stdlib=libc++" + -DCMAKE_EXE_LINKER_FLAGS="-lc++abi" + -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_clang_cxx${CXX_STANDARD} + COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_clang_cxx${CXX_STANDARD} + COMMAND cd ${PROJECT_BINARY_DIR}/build_clang_cxx${CXX_STANDARD} && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure + COMMENT "Compile and test with Clang for C++${CXX_STANDARD} (libc++)" + ) endforeach() ############################################################################### @@ -505,6 +184,20 @@ add_custom_target(ci_test_diagnostics COMMENT "Compile and test with improved diagnostics enabled" ) +############################################################################### +# Enable diagnostic positions support. +############################################################################### + +add_custom_target(ci_test_diagnostic_positions + COMMAND ${CMAKE_COMMAND} + -DCMAKE_BUILD_TYPE=Debug -GNinja + -DJSON_BuildTests=ON -DJSON_Diagnostic_Positions=ON + -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_diagnostic_positions + COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_diagnostic_positions + COMMAND cd ${PROJECT_BINARY_DIR}/build_diagnostic_positions && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure + COMMENT "Compile and test with diagnostic positions enabled" +) + ############################################################################### # Enable legacy discarded value comparison. ############################################################################### @@ -553,8 +246,8 @@ add_custom_target(ci_test_coverage COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_coverage32 COMMAND cd ${PROJECT_BINARY_DIR}/build_coverage32 && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure - COMMAND ${LCOV_TOOL} --directory . --capture --output-file json.info --rc lcov_branch_coverage=1 - COMMAND ${LCOV_TOOL} -e json.info ${SRC_FILES} --output-file json.info.filtered --rc lcov_branch_coverage=1 + COMMAND ${LCOV_TOOL} --directory . --capture --output-file json.info --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 --ignore-errors mismatch --ignore-errors unused + COMMAND ${LCOV_TOOL} -e json.info ${SRC_FILES} --output-file json.info.filtered --rc branch_coverage=1 --ignore-errors unused COMMAND ${CMAKE_SOURCE_DIR}/tests/thirdparty/imapdl/filterbr.py json.info.filtered > json.info.filtered.noexcept COMMAND genhtml --title "JSON for Modern C++" --legend --demangle-cpp --output-directory html --show-details --branch-coverage json.info.filtered.noexcept @@ -581,8 +274,6 @@ add_custom_target(ci_test_clang_sanitizer # Check if header is amalgamated and sources are properly indented. ############################################################################### -set(ASTYLE_FLAGS --style=allman --indent=spaces=4 --indent-modifiers --indent-switches --indent-preproc-block --indent-preproc-define --indent-col1-comments --pad-oper --pad-header --align-pointer=type --align-reference=type --add-brackets --convert-tabs --close-templates --lineend=linux --preserve-date --formatted) - file(GLOB_RECURSE INDENT_FILES ${PROJECT_SOURCE_DIR}/include/nlohmann/*.hpp ${PROJECT_SOURCE_DIR}/tests/src/*.cpp @@ -598,14 +289,18 @@ add_custom_target(ci_test_amalgamation COMMAND cp ${include_dir}/json.hpp ${include_dir}/json.hpp~ COMMAND cp ${include_dir}/json_fwd.hpp ${include_dir}/json_fwd.hpp~ + COMMAND ${Python3_EXECUTABLE} -mvenv venv_astyle + COMMAND venv_astyle/bin/pip3 --quiet install -r ${CMAKE_SOURCE_DIR}/tools/astyle/requirements.txt + COMMAND venv_astyle/bin/astyle --version + COMMAND ${Python3_EXECUTABLE} ${tool_dir}/amalgamate.py -c ${tool_dir}/config_json.json -s . COMMAND ${Python3_EXECUTABLE} ${tool_dir}/amalgamate.py -c ${tool_dir}/config_json_fwd.json -s . - COMMAND ${ASTYLE_TOOL} ${ASTYLE_FLAGS} --suffix=none --quiet ${include_dir}/json.hpp ${include_dir}/json_fwd.hpp + COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=none ${include_dir}/json.hpp ${include_dir}/json_fwd.hpp COMMAND diff ${include_dir}/json.hpp~ ${include_dir}/json.hpp COMMAND diff ${include_dir}/json_fwd.hpp~ ${include_dir}/json_fwd.hpp - COMMAND ${ASTYLE_TOOL} ${ASTYLE_FLAGS} ${INDENT_FILES} + COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=orig ${INDENT_FILES} COMMAND for FILE in `find . -name '*.orig'`\; do false \; done WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} @@ -617,7 +312,7 @@ add_custom_target(ci_test_amalgamation ############################################################################### add_custom_target(ci_test_single_header - COMMAND CXX=${GCC_TOOL} CXXFLAGS="${GCC_CXXFLAGS}" ${CMAKE_COMMAND} + COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug -GNinja -DJSON_BuildTests=ON -DJSON_MultipleHeaders=OFF -DJSON_FastTests=ON -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_single_header @@ -660,7 +355,16 @@ add_custom_target(ci_clang_analyze ############################################################################### add_custom_target(ci_cppcheck - COMMAND ${CPPCHECK_TOOL} --enable=warning --suppress=missingReturn --inline-suppr --inconclusive --force --std=c++11 ${PROJECT_SOURCE_DIR}/single_include/nlohmann/json.hpp --error-exitcode=1 + COMMAND mkdir -p cppcheck + COMMAND clang -dM -E -x c++ -std=c++11 ${CMAKE_SOURCE_DIR}/include/nlohmann/thirdparty/hedley/hedley.hpp > default_defines.hpp 2> /dev/null + COMMAND ${Python3_EXECUTABLE} -mvenv venv_cppcheck + COMMAND venv_cppcheck/bin/pip3 --quiet install -r ${CMAKE_SOURCE_DIR}/cmake/requirements/requirements-cppcheck.txt + COMMAND venv_cppcheck/bin/cppcheck --enable=warning --check-level=exhaustive --inline-suppr --inconclusive --force + --std=c++11 ${PROJECT_SOURCE_DIR}/include/nlohmann/json.hpp -I ${CMAKE_SOURCE_DIR}/include + --error-exitcode=1 --relative-paths=${PROJECT_SOURCE_DIR} -j ${N} --include=default_defines.hpp + --cppcheck-build-dir=cppcheck --check-level=exhaustive + -UJSON_CATCH_USER -UJSON_TRY_USER -UJSON_ASSERT -UJSON_INTERNAL_CATCH -UJSON_THROW + -DJSON_HAS_CPP_11 -UJSON_HAS_CPP_14 -UJSON_HAS_CPP_17 -UJSON_HAS_CPP_20 -UJSON_HAS_THREE_WAY_COMPARISON COMMENT "Check code with Cppcheck" ) @@ -670,7 +374,7 @@ add_custom_target(ci_cppcheck add_custom_target(ci_cpplint COMMAND ${Python3_EXECUTABLE} -mvenv venv_cpplint - COMMAND venv_cpplint/bin/pip3 --quiet install cpplint + COMMAND venv_cpplint/bin/pip3 --quiet install -r ${CMAKE_SOURCE_DIR}/cmake/requirements/requirements-cpplint.txt COMMAND venv_cpplint/bin/cpplint --filter=-whitespace,-legal,-runtime/references,-runtime/explicit,-runtime/indentation_namespace,-readability/casting,-readability/nolint --quiet --recursive ${SRC_FILES} COMMENT "Check code with cpplint" WORKING_DIRECTORY ${PROJECT_BINARY_DIR} @@ -837,6 +541,11 @@ add_custom_target(ci_benchmarks # CMake flags ############################################################################### +# we test the project with different CMake versions: +# - CMake 3.5 (the earliest supported) +# - CMake 3.31.6 (the latest 3.x release) +# - CMake 4.0.0 (the latest release) + function(ci_get_cmake version var) set(${var} ${PROJECT_BINARY_DIR}/cmake-${version}/bin/cmake) add_custom_command( @@ -844,26 +553,30 @@ function(ci_get_cmake version var) COMMAND wget -nc https://github.com/Kitware/CMake/releases/download/v${version}/cmake-${version}.tar.gz COMMAND tar xfz cmake-${version}.tar.gz COMMAND rm cmake-${version}.tar.gz - COMMAND ${CMAKE_COMMAND} -S cmake-${version} -B cmake-${version} - COMMAND ${CMAKE_COMMAND} --build cmake-${version} --parallel 10 + # -DCMAKE_POLICY_VERSION_MINIMUM=3.5 required to compile older CMake versions with CMake 4.0.0 + COMMAND cmake -S cmake-${version} -B cmake-${version} -DCMAKE_POLICY_VERSION_MINIMUM=3.5 + COMMAND cmake --build cmake-${version} --parallel 10 WORKING_DIRECTORY ${PROJECT_BINARY_DIR} COMMENT "Download CMake ${version}" ) set(${var} ${${var}} PARENT_SCOPE) endfunction() -ci_get_cmake(3.1.0 CMAKE_3_1_0_BINARY) -ci_get_cmake(3.13.0 CMAKE_3_13_0_BINARY) +ci_get_cmake(3.5.0 CMAKE_3_5_0_BINARY) +ci_get_cmake(3.31.6 CMAKE_3_31_6_BINARY) +ci_get_cmake(4.0.0 CMAKE_4_0_0_BINARY) -set(JSON_CMAKE_FLAGS_3_1_0 JSON_Diagnostics JSON_GlobalUDLs JSON_ImplicitConversions JSON_DisableEnumSerialization +# the tests require CMake 3.13 or later, so they are excluded for CMake 3.5.0 +set(JSON_CMAKE_FLAGS_3_5_0 JSON_Diagnostics JSON_Diagnostic_Positions JSON_GlobalUDLs JSON_ImplicitConversions JSON_DisableEnumSerialization JSON_LegacyDiscardedValueComparison JSON_Install JSON_MultipleHeaders JSON_SystemInclude JSON_Valgrind) -set(JSON_CMAKE_FLAGS_3_13_0 JSON_BuildTests) +set(JSON_CMAKE_FLAGS_3_31_6 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_31_6}) +set(JSON_CMAKE_FLAGS_4_0_0 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0}) function(ci_add_cmake_flags_targets flag min_version) string(TOLOWER "ci_cmake_flag_${flag}" flag_target) string(REPLACE . _ min_version_var ${min_version}) set(cmake_binary ${CMAKE_${min_version_var}_BINARY}) - add_custom_target(${flag_target} + add_custom_target(${flag_target}_${min_version}_2 COMMENT "Check CMake flag ${flag} (CMake ${CMAKE_VERSION})" COMMAND ${CMAKE_COMMAND} -Werror=dev @@ -883,12 +596,16 @@ function(ci_add_cmake_flags_targets flag min_version) set(JSON_CMAKE_FLAG_BUILD_DIRS ${JSON_CMAKE_FLAG_BUILD_DIRS} PARENT_SCOPE) endfunction() -foreach(JSON_CMAKE_FLAG ${JSON_CMAKE_FLAGS_3_1_0}) - ci_add_cmake_flags_targets(${JSON_CMAKE_FLAG} 3.1.0) +foreach(JSON_CMAKE_FLAG ${JSON_CMAKE_FLAGS_3_5_0}) + ci_add_cmake_flags_targets(${JSON_CMAKE_FLAG} 3.5.0) endforeach() -foreach(JSON_CMAKE_FLAG ${JSON_CMAKE_FLAGS_3_13_0}) - ci_add_cmake_flags_targets(${JSON_CMAKE_FLAG} 3.13.0) +foreach(JSON_CMAKE_FLAG ${JSON_CMAKE_FLAGS_3_31_6}) + ci_add_cmake_flags_targets(${JSON_CMAKE_FLAG} 3.31.6) +endforeach() + +foreach(JSON_CMAKE_FLAG ${JSON_CMAKE_FLAGS_4_0_0}) + ci_add_cmake_flags_targets(${JSON_CMAKE_FLAG} 4.0.0) endforeach() add_custom_target(ci_cmake_flags @@ -900,7 +617,7 @@ add_custom_target(ci_cmake_flags # Use more installed compilers. ############################################################################### -foreach(COMPILER g++-4.8 g++-4.9 g++-5 g++-6 g++-7 g++-8 g++-9 g++-10 g++-11 clang++-3.5 clang++-3.6 clang++-3.7 clang++-3.8 clang++-3.9 clang++-4.0 clang++-5.0 clang++-6.0 clang++-7 clang++-8 clang++-9 clang++-10 clang++-11 clang++-12 clang++-13 clang++-14 clang++-15 clang++-16 clang++-17) +foreach(COMPILER g++-4.8 g++-4.9 g++-5 g++-6 g++-7 g++-8 g++-9 g++-10 g++-11 clang++-3.5 clang++-3.6 clang++-3.7 clang++-3.8 clang++-3.9 clang++-4.0 clang++-5.0 clang++-6.0 clang++-7 clang++-8 clang++-9 clang++-10 clang++-11 clang++-12 clang++-13 clang++-14 clang++-15 clang++-16 clang++-17 clang++-18 clang++-19 clang++-20) find_program(COMPILER_TOOL NAMES ${COMPILER}) if (COMPILER_TOOL) unset(ADDITIONAL_FLAGS) @@ -942,6 +659,18 @@ add_custom_target(ci_cuda_example COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_cuda_example ) +############################################################################### +# C++ 20 modules +############################################################################### + +add_custom_target(ci_module_cpp20 + COMMAND ${CMAKE_COMMAND} + -DCMAKE_BUILD_TYPE=Debug -GNinja + -DJSON_CI=ON -DNLOHMANN_JSON_BUILD_MODULES=ON -DJSON_Install=ON + -S${PROJECT_SOURCE_DIR}/tests/module_cpp20 -B${PROJECT_BINARY_DIR}/ci_module_cpp20 + COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/ci_module_cpp20 +) + ############################################################################### # Intel C++ Compiler ############################################################################### @@ -957,6 +686,17 @@ add_custom_target(ci_icpc COMMENT "Compile and test with ICPC" ) +############################################################################### +# REUSE +############################################################################### + +add_custom_target(ci_reuse_compliance + COMMAND ${Python3_EXECUTABLE} -mvenv venv_reuse + COMMAND venv_reuse/bin/pip3 --quiet install -r ${PROJECT_SOURCE_DIR}/cmake/requirements/requirements-reuse.txt + COMMAND venv_reuse/bin/reuse --root ${PROJECT_SOURCE_DIR} lint + COMMENT "Check REUSE specification compliance" +) + ############################################################################### # test documentation ############################################################################### @@ -967,10 +707,12 @@ add_custom_target(ci_test_examples COMMENT "Check that all examples compile and create the desired output" ) -add_custom_target(ci_test_api_documentation - COMMAND ${Python3_EXECUTABLE} scripts/check_structure.py +add_custom_target(ci_test_build_documentation + COMMAND ${Python3_EXECUTABLE} -mvenv venv + COMMAND venv/bin/pip3 --quiet install -r requirements.txt + COMMAND make build WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/docs/mkdocs - COMMENT "Lint the API documentation" + COMMENT "Build the documentation" ) ############################################################################### @@ -978,6 +720,6 @@ add_custom_target(ci_test_api_documentation ############################################################################### add_custom_target(ci_clean - COMMAND rm -fr ${PROJECT_BINARY_DIR}/build_* cmake-3.1.0-Darwin64 ${JSON_CMAKE_FLAG_BUILD_DIRS} ${single_binaries} + COMMAND rm -fr ${PROJECT_BINARY_DIR}/build_* cmake-3.5.0-Darwin64 ${JSON_CMAKE_FLAG_BUILD_DIRS} ${single_binaries} COMMENT "Clean generated directories" ) diff --git a/cmake/clang_flags.cmake b/cmake/clang_flags.cmake new file mode 100644 index 0000000000..b3dfa31440 --- /dev/null +++ b/cmake/clang_flags.cmake @@ -0,0 +1,22 @@ +# Ignored Clang warnings: +# -Wno-c++98-compat The library targets C++11. +# -Wno-c++98-compat-pedantic The library targets C++11. +# -Wno-deprecated-declarations The library contains annotations for deprecated functions. +# -Wno-extra-semi-stmt The library uses assert which triggers this warning. +# -Wno-padded We do not care about padding warnings. +# -Wno-covered-switch-default All switches list all cases and a default case. +# -Wno-unsafe-buffer-usage Otherwise Doctest would not compile. +# -Wno-missing-noreturn We found no way to silence this warning otherwise, see PR #4871 + +set(CLANG_CXXFLAGS + -Werror + -Weverything + -Wno-c++98-compat + -Wno-c++98-compat-pedantic + -Wno-deprecated-declarations + -Wno-extra-semi-stmt + -Wno-padded + -Wno-covered-switch-default + -Wno-unsafe-buffer-usage + -Wno-missing-noreturn +) diff --git a/cmake/detect_libcpp_version.cpp b/cmake/detect_libcpp_version.cpp new file mode 100644 index 0000000000..a39322fcd2 --- /dev/null +++ b/cmake/detect_libcpp_version.cpp @@ -0,0 +1,31 @@ +/* + * Detect used C++ Standard Library + * + * This file is compiled and run via try_run in download_test_data.cmake. + */ + +#include + +// see https://en.cppreference.com/w/cpp/header/ciso646 +#if __cplusplus >= 202002L + #include +#else + #include +#endif + +int main() +{ +#if defined(_LIBCPP_VERSION) + std::printf("LLVM C++ Standard Library (libc++), _LIBCPP_VERSION=%d", _LIBCPP_VERSION); +#elif defined(__GLIBCXX__) + std::printf("GNU C++ Standard Library (libstdc++), __GLIBCXX__=%d", __GLIBCXX__); +#elif defined(_MSVC_STL_VERSION) + std::printf("Microsoft C++ Standard Library (MSVC STL), _MSVC_STL_VERSION=%d", _MSVC_STL_VERSION); +#elif defined(_LIBCUDACXX_VERSION) + std::printf("NVIDIA C++ Standard Library (libcudacxx), _LIBCUDACXX_VERSION=%d", _LIBCUDACXX_VERSION); +#elif defined(EASTL_VERSION) + std::printf("Electronic Arts Standard Template Library (EASTL), EASTL_VERSION=%d", EASTL_VERSION); +#else + std::printf("unknown"); +#endif +} diff --git a/cmake/download_test_data.cmake b/cmake/download_test_data.cmake index 1bb998dae6..6f63a08ac6 100644 --- a/cmake/download_test_data.cmake +++ b/cmake/download_test_data.cmake @@ -1,21 +1,33 @@ set(JSON_TEST_DATA_URL https://github.com/nlohmann/json_test_data) set(JSON_TEST_DATA_VERSION 3.1.0) +include(ExternalProject) + # if variable is set, use test data from given directory rather than downloading them if(JSON_TestDataDirectory) message(STATUS "Using test data in ${JSON_TestDataDirectory}.") add_custom_target(download_test_data) file(WRITE ${CMAKE_BINARY_DIR}/include/test_data.hpp "#define TEST_DATA_DIRECTORY \"${JSON_TestDataDirectory}\"\n") else() - find_package(Git) + # create a header with the path to the downloaded test data + file(WRITE ${CMAKE_BINARY_DIR}/include/test_data.hpp "#define TEST_DATA_DIRECTORY \"${CMAKE_BINARY_DIR}/test_files\"\n") + + # download test data from GitHub release + ExternalProject_Add(download_test_data_project + URL "${JSON_TEST_DATA_URL}/archive/refs/tags/v${JSON_TEST_DATA_VERSION}.zip" + SOURCE_DIR "${CMAKE_BINARY_DIR}/test_files" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD TRUE + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + EXCLUDE_FROM_ALL TRUE + ) + # target to download test data add_custom_target(download_test_data - COMMAND test -d json_test_data || ${GIT_EXECUTABLE} clone -c advice.detachedHead=false --branch v${JSON_TEST_DATA_VERSION} ${JSON_TEST_DATA_URL}.git --quiet --depth 1 - COMMENT "Downloading test data from ${JSON_TEST_DATA_URL} (v${JSON_TEST_DATA_VERSION})" - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + DEPENDS download_test_data_project ) - # create a header with the path to the downloaded test data - file(WRITE ${CMAKE_BINARY_DIR}/include/test_data.hpp "#define TEST_DATA_DIRECTORY \"${CMAKE_BINARY_DIR}/json_test_data\"\n") endif() # determine the operating system (for debug and support purposes) @@ -54,3 +66,22 @@ else() endif() string(REGEX REPLACE "[ ]*\n" "; " CXX_VERSION_RESULT "${CXX_VERSION_RESULT}") message(STATUS "Compiler: ${CXX_VERSION_RESULT}") + +# determine used C++ standard library (for debug and support purposes) +if(CMAKE_CROSSCOMPILING) + set(LIBCPP_VERSION_OUTPUT_CACHED "could not be detected due to cross-compiling" CACHE STRING "Detected C++ standard library version") +endif() +if(NOT DEFINED LIBCPP_VERSION_OUTPUT_CACHED) + try_run(RUN_RESULT_VAR COMPILE_RESULT_VAR + "${CMAKE_BINARY_DIR}" SOURCES "${CMAKE_SOURCE_DIR}/cmake/detect_libcpp_version.cpp" + RUN_OUTPUT_VARIABLE LIBCPP_VERSION_OUTPUT + COMPILE_OUTPUT_VARIABLE LIBCPP_VERSION_COMPILE_OUTPUT + ) + if(NOT LIBCPP_VERSION_OUTPUT) + set(LIBCPP_VERSION_OUTPUT "Unknown") + message(AUTHOR_WARNING "Failed to compile cmake/detect_libcpp_version to detect the used C++ standard library. This does not affect the library or the test cases. Please still create an issue at https://github.com/nlohmann/json to investigate this.\n${LIBCPP_VERSION_COMPILE_OUTPUT}") + endif() + set(LIBCPP_VERSION_OUTPUT_CACHED "${LIBCPP_VERSION_OUTPUT}" CACHE STRING "Detected C++ standard library version") +endif() + +message(STATUS "C++ standard library: ${LIBCPP_VERSION_OUTPUT_CACHED}") diff --git a/cmake/gcc_flags.cmake b/cmake/gcc_flags.cmake new file mode 100644 index 0000000000..34c168cee5 --- /dev/null +++ b/cmake/gcc_flags.cmake @@ -0,0 +1,363 @@ +# Warning flags determined for GCC 15.1.0 with https://github.com/nlohmann/gcc_flags: +# Ignored GCC warnings: +# -Wno-abi-tag We do not care about ABI tags. +# -Wno-aggregate-return The library uses aggregate returns. +# -Wno-long-long The library uses the long long type to interface with system functions. +# -Wno-namespaces The library uses namespaces. +# -Wno-nrvo Doctest triggers this warning. +# -Wno-padded We do not care about padding warnings. +# -Wno-system-headers We do not care about warnings in system headers. +# -Wno-templates The library uses templates. + +set(GCC_CXXFLAGS + -pedantic + -Werror + --all-warnings + --extra-warnings + -W + -WNSObject-attribute + -Wno-abi-tag + -Waddress + -Waddress-of-packed-member + -Wno-aggregate-return + -Waggressive-loop-optimizations + -Waligned-new=all + -Wall + -Walloc-size + -Walloc-zero + -Walloca + -Wanalyzer-allocation-size + -Wanalyzer-deref-before-check + -Wanalyzer-double-fclose + -Wanalyzer-double-free + -Wanalyzer-exposure-through-output-file + -Wanalyzer-exposure-through-uninit-copy + -Wanalyzer-fd-access-mode-mismatch + -Wanalyzer-fd-double-close + -Wanalyzer-fd-leak + -Wanalyzer-fd-phase-mismatch + -Wanalyzer-fd-type-mismatch + -Wanalyzer-fd-use-after-close + -Wanalyzer-fd-use-without-check + -Wanalyzer-file-leak + -Wanalyzer-free-of-non-heap + -Wanalyzer-imprecise-fp-arithmetic + -Wanalyzer-infinite-loop + -Wanalyzer-infinite-recursion + -Wanalyzer-jump-through-null + -Wanalyzer-malloc-leak + -Wanalyzer-mismatching-deallocation + -Wanalyzer-null-argument + -Wanalyzer-null-dereference + -Wanalyzer-out-of-bounds + -Wanalyzer-overlapping-buffers + -Wanalyzer-possible-null-argument + -Wanalyzer-possible-null-dereference + -Wanalyzer-putenv-of-auto-var + -Wanalyzer-shift-count-negative + -Wanalyzer-shift-count-overflow + -Wanalyzer-stale-setjmp-buffer + -Wanalyzer-symbol-too-complex + -Wanalyzer-tainted-allocation-size + -Wanalyzer-tainted-array-index + -Wanalyzer-tainted-assertion + -Wanalyzer-tainted-divisor + -Wanalyzer-tainted-offset + -Wanalyzer-tainted-size + -Wanalyzer-too-complex + -Wanalyzer-undefined-behavior-ptrdiff + -Wanalyzer-undefined-behavior-strtok + -Wanalyzer-unsafe-call-within-signal-handler + -Wanalyzer-use-after-free + -Wanalyzer-use-of-pointer-in-stale-stack-frame + -Wanalyzer-use-of-uninitialized-value + -Wanalyzer-va-arg-type-mismatch + -Wanalyzer-va-list-exhausted + -Wanalyzer-va-list-leak + -Wanalyzer-va-list-use-after-va-end + -Wanalyzer-write-to-const + -Wanalyzer-write-to-string-literal + -Warith-conversion + -Warray-bounds=2 + -Warray-compare + -Warray-parameter=2 + -Wattribute-alias=2 + -Wattribute-warning + -Wattributes + -Wbool-compare + -Wbool-operation + -Wbuiltin-declaration-mismatch + -Wbuiltin-macro-redefined + -Wc++0x-compat + -Wc++11-compat + -Wc++11-extensions + -Wc++14-compat + -Wc++14-extensions + -Wc++17-compat + -Wc++17-extensions + -Wc++1z-compat + -Wc++20-compat + -Wc++20-extensions + -Wc++23-extensions + -Wc++26-extensions + -Wc++2a-compat + -Wcalloc-transposed-args + -Wcannot-profile + -Wcast-align + -Wcast-align=strict + -Wcast-function-type + -Wcast-qual + -Wcast-user-defined + -Wcatch-value=3 + -Wchanges-meaning + -Wchar-subscripts + -Wclass-conversion + -Wclass-memaccess + -Wclobbered + -Wcomma-subscript + -Wcomment + -Wcomments + -Wcomplain-wrong-lang + -Wconditionally-supported + -Wconversion + -Wconversion-null + -Wcoverage-invalid-line-number + -Wcoverage-mismatch + -Wcoverage-too-many-conditions + -Wcoverage-too-many-paths + -Wcpp + -Wctad-maybe-unsupported + -Wctor-dtor-privacy + -Wdangling-else + -Wdangling-pointer=2 + -Wdangling-reference + -Wdate-time + -Wdefaulted-function-deleted + -Wdelete-incomplete + -Wdelete-non-virtual-dtor + -Wdeprecated + -Wdeprecated-copy + -Wdeprecated-copy-dtor + -Wdeprecated-declarations + -Wdeprecated-enum-enum-conversion + -Wdeprecated-enum-float-conversion + -Wdeprecated-literal-operator + -Wdeprecated-variadic-comma-omission + -Wdisabled-optimization + -Wdiv-by-zero + -Wdouble-promotion + -Wduplicated-branches + -Wduplicated-cond + -Weffc++ + -Welaborated-enum-base + -Wempty-body + -Wendif-labels + -Wenum-compare + -Wenum-conversion + -Wexceptions + -Wexpansion-to-defined + -Wextra + -Wextra-semi + -Wflex-array-member-not-at-end + -Wfloat-conversion + -Wfloat-equal + -Wformat -Wformat-contains-nul + -Wformat -Wformat-diag + -Wformat -Wformat-extra-args + -Wformat -Wformat-nonliteral + -Wformat -Wformat-overflow=2 + -Wformat -Wformat-security + -Wformat -Wformat-signedness + -Wformat -Wformat-truncation=2 + -Wformat -Wformat-y2k + -Wformat -Wformat-zero-length + -Wformat=2 + -Wframe-address + -Wfree-nonheap-object + -Wglobal-module + -Whardened + -Wheader-guard + -Whsa + -Wif-not-aligned + -Wignored-attributes + -Wignored-qualifiers + -Wimplicit-fallthrough=5 + -Winaccessible-base + -Winfinite-recursion + -Winherited-variadic-ctor + -Winit-list-lifetime + -Winit-self + -Winline + -Wint-in-bool-context + -Wint-to-pointer-cast + -Winterference-size + -Winvalid-constexpr + -Winvalid-imported-macros + -Winvalid-memory-model + -Winvalid-offsetof + -Winvalid-pch + -Winvalid-utf8 + -Wliteral-suffix + -Wlogical-not-parentheses + -Wlogical-op + -Wno-long-long + -Wlto-type-mismatch + -Wmain + -Wmaybe-musttail-local-addr + -Wmaybe-uninitialized + -Wmemset-elt-size + -Wmemset-transposed-args + -Wmisleading-indentation + -Wmismatched-dealloc + -Wmismatched-new-delete + -Wmismatched-tags + -Wmissing-attributes + -Wmissing-braces + -Wmissing-declarations + -Wmissing-field-initializers + -Wmissing-include-dirs + -Wmissing-profile + -Wmissing-requires + -Wmissing-template-keyword + -Wmultichar + -Wmultiple-inheritance + -Wmultistatement-macros + -Wmusttail-local-addr + -Wno-namespaces + -Wnarrowing + -Wnoexcept + -Wnoexcept-type + -Wnon-template-friend + -Wnon-virtual-dtor + -Wnonnull + -Wnonnull-compare + -Wnormalized=nfkc + -Wno-nrvo + -Wnull-dereference + -Wodr + -Wold-style-cast + -Wopenacc-parallelism + -Wopenmp + -Wopenmp-simd + -Woverflow + -Woverlength-strings + -Woverloaded-virtual=2 + -Wpacked + -Wpacked-bitfield-compat + -Wpacked-not-aligned + -Wno-padded + -Wparentheses + -Wpedantic + -Wpessimizing-move + -Wplacement-new=2 + -Wpmf-conversions + -Wpointer-arith + -Wpointer-compare + -Wpragma-once-outside-header + -Wpragmas + -Wprio-ctor-dtor + -Wpsabi + -Wrange-loop-construct + -Wredundant-decls + -Wredundant-move + -Wredundant-tags + -Wregister + -Wreorder + -Wrestrict + -Wreturn-local-addr + -Wreturn-type + -Wscalar-storage-order + -Wself-move + -Wsequence-point + -Wshadow=compatible-local + -Wshadow=global + -Wshadow=local + -Wshift-count-negative + -Wshift-count-overflow + -Wshift-negative-value + -Wshift-overflow=2 + -Wsign-compare + -Wsign-conversion + -Wsign-promo + -Wsized-deallocation + -Wsizeof-array-argument + -Wsizeof-array-div + -Wsizeof-pointer-div + -Wsizeof-pointer-memaccess + -Wstack-protector + -Wstrict-aliasing + -Wstrict-aliasing=3 + -Wstrict-null-sentinel + -Wstrict-overflow + -Wstring-compare + -Wstringop-overflow + -Wstringop-overflow=4 + -Wstringop-overread + -Wstringop-truncation + -Wsubobject-linkage + -Wsuggest-attribute=cold + -Wsuggest-attribute=const + -Wsuggest-attribute=format + -Wsuggest-attribute=malloc + -Wsuggest-attribute=noreturn + -Wsuggest-attribute=pure + -Wsuggest-attribute=returns_nonnull + -Wsuggest-final-methods + -Wsuggest-final-types + -Wsuggest-override + -Wswitch + -Wswitch-bool + -Wswitch-default + -Wswitch-enum + -Wswitch-outside-range + -Wswitch-unreachable + -Wsync-nand + -Wsynth + -Wno-system-headers + -Wtautological-compare + -Wtemplate-body + -Wtemplate-id-cdtor + -Wtemplate-names-tu-local + -Wno-templates + -Wterminate + -Wtrailing-whitespace + -Wtrampolines + -Wtrigraphs + -Wtrivial-auto-var-init + -Wtsan + -Wtype-limits + -Wundef + -Wunicode + -Wuninitialized + -Wunknown-pragmas + -Wunreachable-code + -Wunsafe-loop-optimizations + -Wunused + -Wunused-but-set-parameter + -Wunused-but-set-variable + -Wunused-const-variable=2 + -Wunused-function + -Wunused-label + -Wunused-local-typedefs + -Wunused-macros + -Wunused-parameter + -Wunused-result + -Wunused-value + -Wunused-variable + -Wuse-after-free=3 + -Wuseless-cast + -Wvarargs + -Wvariadic-macros + -Wvector-operation-performance + -Wvexing-parse + -Wvirtual-inheritance + -Wvirtual-move-assign + -Wvla + -Wvla-parameter + -Wvolatile + -Wvolatile-register-var + -Wwrite-strings + -Wxor-used-as-pow + -Wzero-as-null-pointer-constant + -Wzero-length-bounds +) diff --git a/cmake/pkg-config.pc.in b/cmake/pkg-config.pc.in index d36317f09a..21a91a3cf3 100644 --- a/cmake/pkg-config.pc.in +++ b/cmake/pkg-config.pc.in @@ -1,4 +1,7 @@ -Name: ${PROJECT_NAME} +prefix=@CMAKE_INSTALL_PREFIX@ +includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ + +Name: @PROJECT_NAME@ Description: JSON for Modern C++ -Version: ${PROJECT_VERSION} -Cflags: -I${CMAKE_INSTALL_FULL_INCLUDEDIR} +Version: @PROJECT_VERSION@ +Cflags: -I${includedir} diff --git a/cmake/requirements/requirements-cppcheck.txt b/cmake/requirements/requirements-cppcheck.txt new file mode 100644 index 0000000000..9119ad7b6b --- /dev/null +++ b/cmake/requirements/requirements-cppcheck.txt @@ -0,0 +1 @@ +cppcheck==1.5.0 diff --git a/cmake/requirements/requirements-cpplint.txt b/cmake/requirements/requirements-cpplint.txt new file mode 100644 index 0000000000..6aabac21c3 --- /dev/null +++ b/cmake/requirements/requirements-cpplint.txt @@ -0,0 +1 @@ +cpplint==2.0.2 diff --git a/cmake/requirements/requirements-reuse.txt b/cmake/requirements/requirements-reuse.txt new file mode 100644 index 0000000000..4666d51bf8 --- /dev/null +++ b/cmake/requirements/requirements-reuse.txt @@ -0,0 +1 @@ +reuse==6.2.0 diff --git a/docs/Makefile b/docs/Makefile index 35c30daefb..0412fb90a0 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -7,7 +7,7 @@ all: create_output ########################################################################## # where are the example cpp files -EXAMPLES = $(wildcard examples/*.cpp) +EXAMPLES = $(wildcard mkdocs/docs/examples/*.cpp) cxx_standard = $(lastword c++11 $(filter c++%, $(subst ., ,$1))) @@ -37,9 +37,8 @@ check_output: $(EXAMPLES:.cpp=.test) # check output of all stand-alone example files (exclude files with platform-dependent output.) # This target is used in the CI (ci_test_documentation). -check_output_portable: $(filter-out examples/meta.test examples/max_size.test examples/std_hash.test examples/basic_json__CompatibleType.test,$(EXAMPLES:.cpp=.test)) +check_output_portable: $(filter-out mkdocs/docs/examples/meta.test mkdocs/docs/examples/max_size.test mkdocs/docs/examples/std_hash.test mkdocs/docs/examples/basic_json__CompatibleType.test,$(EXAMPLES:.cpp=.test)) clean: rm -fr $(EXAMPLES:.cpp=) $(MAKE) clean -C docset - $(MAKE) clean -C mkdocs diff --git a/docs/README.md b/docs/README.md index b39d54e055..7cca7f12f6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,12 +2,12 @@ ## Generate documentation -Note on documentation: The source files contain links to the online documentation at https://json.nlohmann.me. This URL -contains the most recent documentation and should also be applicable to previous versions; documentation for deprecated -functions is not removed, but marked deprecated. +Note on documentation: The source files contain links to the online documentation at https://json.nlohmann.me. +This URL provides the most recent documentation and also applies to previous versions. Documentation for deprecated +functions is not removed; instead, it is marked as deprecated. -If you want to see the documentation for a specific tag or commit hash, you can generate it as follows (here for tag -`v3.10.2`): +If you want to view the documentation for a specific tag or commit hash, you can generate it locally as follows (example +using tag `v3.10.2`): ```shell git clone https://github.com/nlohmann/json.git @@ -16,5 +16,8 @@ git checkout v3.10.2 make install_venv serve -C docs/mkdocs ``` -Open URL in your browser. Replace from any URL from the source code `https://json.nlohmann.me` -with `http://127.0.0.1:8000` to see the documentation for your tag or commit hash. +Open in your browser. Replace any URL in the source code that points to +`https://json.nlohmann.me` with `http://127.0.0.1:8000` to view the documentation for the selected tag or commit hash. + + + diff --git a/docs/avatars.png b/docs/avatars.png index 25429f34aa..c6efa0c1f5 100644 Binary files a/docs/avatars.png and b/docs/avatars.png differ diff --git a/docs/docset/Makefile b/docs/docset/Makefile index eb1cfd38c0..9f43634629 100644 --- a/docs/docset/Makefile +++ b/docs/docset/Makefile @@ -16,6 +16,7 @@ JSON_for_Modern_C++.docset: Info.plist docSet.dsidx cp icon*.png JSON_for_Modern_C++.docset cp Info.plist JSON_for_Modern_C++.docset/Contents # build and copy documentation + $(MAKE) install_venv -C ../mkdocs $(MAKE) build -C ../mkdocs cp -r ../mkdocs/site/* JSON_for_Modern_C++.docset/Contents/Resources/Documents # patch CSS to hide navigation items diff --git a/docs/docset/docSet.sql b/docs/docset/docSet.sql index ea6b4f2855..38b203d383 100644 --- a/docs/docset/docSet.sql +++ b/docs/docset/docSet.sql @@ -38,6 +38,7 @@ INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::emplace', 'Method INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::emplace_back', 'Method', 'api/basic_json/emplace_back/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::empty', 'Method', 'api/basic_json/empty/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::end', 'Method', 'api/basic_json/end/index.html'); +INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::end_pos', 'Method', 'api/basic_json/end_pos/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::erase', 'Method', 'api/basic_json/erase/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::error_handler_t', 'Enum', 'api/basic_json/error_handler_t/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::exception', 'Class', 'api/basic_json/exception/index.html'); @@ -72,6 +73,7 @@ INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_primitive', 'M INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_string', 'Method', 'api/basic_json/is_string/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_structured', 'Method', 'api/basic_json/is_structured/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::items', 'Method', 'api/basic_json/items/index.html'); +INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::json_base_class_t', 'Type', 'api/basic_json/json_base_class_t/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::json_serializer', 'Class', 'api/basic_json/json_serializer/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::max_size', 'Method', 'api/basic_json/max_size/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::merge_patch', 'Method', 'api/basic_json/merge_patch/index.html'); @@ -107,6 +109,7 @@ INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::rbegin', 'Method' INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::rend', 'Method', 'api/basic_json/rend/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::sax_parse', 'Function', 'api/basic_json/sax_parse/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::size', 'Method', 'api/basic_json/size/index.html'); +INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::start_pos', 'Method', 'api/basic_json/start_pos/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::string_t', 'Type', 'api/basic_json/string_t/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::swap', 'Method', 'api/basic_json/swap/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::type', 'Method', 'api/basic_json/type/index.html'); @@ -175,6 +178,7 @@ INSERT INTO searchIndex(name, type, path) VALUES ('Element Access', 'Guide', 'fe INSERT INTO searchIndex(name, type, path) VALUES ('Element Access: Access with default value: value', 'Guide', 'features/element_access/default_value/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('Element Access: Checked access: at', 'Guide', 'features/element_access/checked_access/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('Element Access: Unchecked access: operator[]', 'Guide', 'features/element_access/unchecked_access/index.html'); +INSERT INTO searchIndex(name, type, path) VALUES ('Exceptions', 'Guide', 'home/exceptions/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('Integration: Migration Guide', 'Guide', 'integration/migration_guide/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('Integration: CMake', 'Guide', 'integration/cmake/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('Integration: Header only', 'Guide', 'integration/index.html'); @@ -201,6 +205,7 @@ INSERT INTO searchIndex(name, type, path) VALUES ('Supported Macros', 'Guide', ' INSERT INTO searchIndex(name, type, path) VALUES ('JSON_ASSERT', 'Macro', 'api/macros/json_assert/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('JSON_CATCH_USER', 'Macro', 'api/macros/json_throw_user/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DIAGNOSTICS', 'Macro', 'api/macros/json_diagnostics/index.html'); +INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DIAGNOSTIC_POSITIONS', 'Macro', 'api/macros/json_diagnostic_positions/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DISABLE_ENUM_SERIALIZATION', 'Macro', 'api/macros/json_disable_enum_serialization/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_11', 'Macro', 'api/macros/json_has_cpp_11/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_14', 'Macro', 'api/macros/json_has_cpp_11/index.html'); @@ -209,6 +214,7 @@ INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_20', 'Macro', 'a INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_EXPERIMENTAL_FILESYSTEM', 'Macro', 'api/macros/json_has_filesystem/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_FILESYSTEM', 'Macro', 'api/macros/json_has_filesystem/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_RANGES', 'Macro', 'api/macros/json_has_ranges/index.html'); +INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_STATIC_RTTI', 'Macro', 'api/macros/json_has_static_rtti/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_THREE_WAY_COMPARISON', 'Macro', 'api/macros/json_has_three_way_comparison/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('JSON_NOEXCEPTION', 'Macro', 'api/macros/json_noexception/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('JSON_NO_IO', 'Macro', 'api/macros/json_no_io/index.html'); @@ -220,6 +226,10 @@ INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_GLOBAL_UDLS', 'Macro INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_IMPLICIT_CONVERSIONS', 'Macro', 'api/macros/json_use_implicit_conversions/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON', 'Macro', 'api/macros/json_use_legacy_discarded_value_comparison/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('Macros', 'Macro', 'api/macros/index.html'); +INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html'); +INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html'); +INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html'); +INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE', 'Macro', 'api/macros/nlohmann_define_type_intrusive/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT', 'Macro', 'api/macros/nlohmann_define_type_intrusive/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE', 'Macro', 'api/macros/nlohmann_define_type_non_intrusive/index.html'); diff --git a/docs/docset/docset.json b/docs/docset/docset.json index bc08d283d3..15cf4a7b19 100644 --- a/docs/docset/docset.json +++ b/docs/docset/docset.json @@ -1,6 +1,6 @@ { "name": "JSON for Modern C++", - "version": "3.11.3", + "version": "3.12.0", "archive": "JSON_for_Modern_C++.tgz", "author": { "name": "Niels Lohmann", diff --git a/docs/examples/nlohmann_json_namespace.output b/docs/examples/nlohmann_json_namespace.output deleted file mode 100644 index 5565deafd2..0000000000 --- a/docs/examples/nlohmann_json_namespace.output +++ /dev/null @@ -1 +0,0 @@ -nlohmann::json_abi_v3_11_3 diff --git a/docs/examples/nlohmann_json_version.output b/docs/examples/nlohmann_json_version.output deleted file mode 100644 index 75dbe84781..0000000000 --- a/docs/examples/nlohmann_json_version.output +++ /dev/null @@ -1 +0,0 @@ -JSON for Modern C++ version 3.11.3 diff --git a/docs/json.gif b/docs/json.gif index 8b86b03757..c708379720 100644 Binary files a/docs/json.gif and b/docs/json.gif differ diff --git a/docs/mkdocs/Makefile b/docs/mkdocs/Makefile index d3356b8202..8c72b0e807 100644 --- a/docs/mkdocs/Makefile +++ b/docs/mkdocs/Makefile @@ -1,37 +1,45 @@ # serve the site locally -serve: prepare_files style_check +serve: style_check venv/bin/mkdocs serve -serve_dirty: prepare_files style_check +serve_dirty: style_check venv/bin/mkdocs serve --dirtyreload -build: prepare_files style_check +# This target is used in the CI (ci_test_build_documentation). +# This target is used by the docset Makefile. +build: style_check venv/bin/mkdocs build -# create files that are not versioned inside the mkdocs folder (images, examples) -prepare_files: clean - mkdir docs/examples - cp -r ../json.gif docs/images - cp -r ../examples/*.cpp ../examples/*.output docs/examples - style_check: - @cd docs ; python3 ../scripts/check_structure.py + @cd docs ; ../venv/bin/python3 ../scripts/check_structure.py + +# check the links in the documentation files in docs/mkdocs +link_check: + ENABLED_HTMLPROOFER=true venv/bin/mkdocs build -# clean subfolders -clean: - rm -fr docs/images/json.gif docs/examples +# check the links in all other Markdown files +link_check_markdowns: + npx markdown-link-check --progress --alive 200,429 ../../README.md ../../FILES.md ../../.github/*.md # publish site to GitHub pages (not working in GitHub Actions; need special action) -publish: prepare_files +publish: venv/bin/mkdocs gh-deploy --clean --force # install a Python virtual environment +# This target is used by the docset Makefile. install_venv: requirements.txt python3 -mvenv venv venv/bin/pip install --upgrade pip - venv/bin/pip install wheel venv/bin/pip install -r requirements.txt # uninstall the virtual environment -uninstall_venv: clean +uninstall_venv: rm -fr venv + +update_requirements: + rm -fr venv_small + python3 -mvenv venv_small + venv_small/bin/pip3 install pur + venv_small/bin/pur -r requirements.txt + rm -fr venv_small venv + make install_venv diff --git a/docs/mkdocs/docs/api/adl_serializer/from_json.md b/docs/mkdocs/docs/api/adl_serializer/from_json.md index 176290e24e..2f49e4b802 100644 --- a/docs/mkdocs/docs/api/adl_serializer/from_json.md +++ b/docs/mkdocs/docs/api/adl_serializer/from_json.md @@ -37,7 +37,7 @@ Copy of the JSON value, converted to `ValueType` ??? example "Example: (1) Default-constructible type" The example below shows how a `from_json` function can be implemented for a user-defined type. This function is - called by the `adl_serializer` when `template get()` is called. + called by the `adl_serializer` when `get()` is called. ```cpp --8<-- "examples/from_json__default_constructible.cpp" diff --git a/docs/mkdocs/docs/api/adl_serializer/index.md b/docs/mkdocs/docs/api/adl_serializer/index.md index 95f35cddf0..e83b66deca 100644 --- a/docs/mkdocs/docs/api/adl_serializer/index.md +++ b/docs/mkdocs/docs/api/adl_serializer/index.md @@ -8,7 +8,7 @@ struct adl_serializer; Serializer that uses ADL ([Argument-Dependent Lookup](https://en.cppreference.com/w/cpp/language/adl)) to choose `to_json`/`from_json` functions from the types' namespaces. -It is implemented similar to +It is implemented similarly to ```cpp template diff --git a/docs/mkdocs/docs/api/basic_json/accept.md b/docs/mkdocs/docs/api/basic_json/accept.md index 1c806e82f4..5f9bac0536 100644 --- a/docs/mkdocs/docs/api/basic_json/accept.md +++ b/docs/mkdocs/docs/api/basic_json/accept.md @@ -4,12 +4,14 @@ // (1) template static bool accept(InputType&& i, - const bool ignore_comments = false); + const bool ignore_comments = false, + const bool ignore_trailing_commas = false); // (2) template 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. @@ -17,10 +19,10 @@ Checks whether the input is valid JSON. 1. Reads from a compatible input. 2. Reads from a pair of character iterators - The value_type of the iterator must be an integral type with size of 1, 2 or 4 bytes, which will be interpreted - respectively as UTF-8, UTF-16 and UTF-32. + The value_type of the iterator must be an integral type with a size of 1, 2, or 4 bytes, which will be interpreted + respectively as UTF-8, UTF-16, and UTF-32. -Unlike the [`parse`](parse.md) function, this function neither throws an exception in case of invalid JSON input +Unlike the [`parse()`](parse.md) function, this function neither throws an exception in case of invalid JSON input (i.e., a parse error) nor creates diagnostic information. ## Template parameters @@ -29,9 +31,9 @@ Unlike the [`parse`](parse.md) function, this function neither throws an excepti : A compatible input, for instance: - an `std::istream` object - - a `FILE` pointer (must not be null) + - a `#!c FILE` pointer (throws if null) - a C-style array of characters - - a pointer to a null-terminated string of single byte characters + - a pointer to a null-terminated string of single byte characters (throws if null) - a `std::string` - an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of iterators. @@ -50,11 +52,15 @@ Unlike the [`parse`](parse.md) function, this function neither throws an excepti : 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 start of character range +: iterator to the start of the character range `last` (in) -: iterator to end of character range +: iterator to the end of the character range ## Return value @@ -64,18 +70,17 @@ Whether the input is valid JSON. Strong guarantee: if an exception is thrown, there are no changes in the JSON value. +## Exceptions + +Throws [`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101) in case of an empty input like a null `#!c FILE*` or `#!c char*` pointer. + ## Complexity Linear in the length of the input. The parser is a predictive LL(1) parser. ## Notes -(1) A UTF-8 byte order mark is silently ignored. - -!!! danger "Runtime assertion" - - The precondition that a passed `#!cpp FILE` pointer must not be null is enforced with a - [runtime assertion](../../features/assertions.md). +A UTF-8 byte order mark is silently ignored. ## Examples @@ -102,6 +107,8 @@ Linear in the length of the input. The parser is a predictive LL(1) parser. - 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" diff --git a/docs/mkdocs/docs/api/basic_json/at.md b/docs/mkdocs/docs/api/basic_json/at.md index 5e95045087..800b46eb3c 100644 --- a/docs/mkdocs/docs/api/basic_json/at.md +++ b/docs/mkdocs/docs/api/basic_json/at.md @@ -59,29 +59,29 @@ Strong exception safety: if an exception occurs, the original value stays intact 1. The function can throw the following exceptions: - Throws [`type_error.304`](../../home/exceptions.md#jsonexceptiontype_error304) if the JSON value is not an array; - in this case, calling `at` with an index makes no sense. See example below. + in this case, calling `at` with an index makes no sense. See the example below. - Throws [`out_of_range.401`](../../home/exceptions.md#jsonexceptionout_of_range401) if the index `idx` is out of - range of the array; that is, `idx >= size()`. See example below. + range of the array; that is, `idx >= size()`. See the example below. 2. The function can throw the following exceptions: - Throws [`type_error.304`](../../home/exceptions.md#jsonexceptiontype_error304) if the JSON value is not an object; - in this case, calling `at` with a key makes no sense. See example below. + in this case, calling `at` with a key makes no sense. See the example below. - Throws [`out_of_range.403`](../../home/exceptions.md#jsonexceptionout_of_range403) if the key `key` is not - stored in the object; that is, `find(key) == end()`. See example below. + stored in the object; that is, `find(key) == end()`. See the example below. 3. See 2. 4. The function can throw the following exceptions: - Throws [`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106) if an array index in the passed - JSON pointer `ptr` begins with '0'. See example below. + JSON pointer `ptr` begins with '0'. See the example below. - Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index in the passed - JSON pointer `ptr` is not a number. See example below. + JSON pointer `ptr` is not a number. See the example below. - Throws [`out_of_range.401`](../../home/exceptions.md#jsonexceptionout_of_range401) if an array index in the passed - JSON pointer `ptr` is out of range. See example below. + JSON pointer `ptr` is out of range. See the example below. - Throws [`out_of_range.402`](../../home/exceptions.md#jsonexceptionout_of_range402) if the array index '-' is used in the passed JSON pointer `ptr`. As `at` provides checked access (and no elements are implicitly inserted), the - index '-' is always invalid. See example below. + index '-' is always invalid. See the example below. - Throws [`out_of_range.403`](../../home/exceptions.md#jsonexceptionout_of_range403) if the JSON pointer describes a - key of an object which cannot be found. See example below. + key of an object which cannot be found. See the example below. - Throws [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) if the JSON pointer `ptr` can - not be resolved. See example below. + not be resolved. See the example below. ## Complexity @@ -215,8 +215,8 @@ Strong exception safety: if an exception occurs, the original value stays intact ## See also - documentation on [checked access](../../features/element_access/checked_access.md) -- see [`operator[]`](operator%5B%5D.md) for unchecked access by reference -- see [`value`](value.md) for access with default value +- [`operator[]`](operator%5B%5D.md) for unchecked access by reference +- [`value`](value.md) for access with default value ## Version history diff --git a/docs/mkdocs/docs/api/basic_json/back.md b/docs/mkdocs/docs/api/basic_json/back.md index 1a715284dc..b717e16e08 100644 --- a/docs/mkdocs/docs/api/basic_json/back.md +++ b/docs/mkdocs/docs/api/basic_json/back.md @@ -17,8 +17,8 @@ return *tmp; ## Return value -In case of a structured type (array or object), a reference to the last element is returned. In case of number, string, -boolean, or binary values, a reference to the value is returned. +In the case of a structured type (array or object), a reference to the last element is returned. In the case of number, +string, boolean, or binary values, a reference to the value is returned. ## Exception safety diff --git a/docs/mkdocs/docs/api/basic_json/basic_json.md b/docs/mkdocs/docs/api/basic_json/basic_json.md index e2e73612c3..83c50007ee 100644 --- a/docs/mkdocs/docs/api/basic_json/basic_json.md +++ b/docs/mkdocs/docs/api/basic_json/basic_json.md @@ -74,7 +74,7 @@ basic_json(basic_json&& other) noexcept; - **boolean**: `boolean_t` / `bool` can be used. - **binary**: `binary_t` / `std::vector` may be used; unfortunately because string literals cannot be distinguished from binary character arrays by the C++ type system, all types compatible with `const char*` will be - directed to the string constructor instead. This is both for backwards compatibility, and due to the fact that a + directed to the string constructor instead. This is both for backwards compatibility and due to the fact that a binary type is not a standard JSON type. See the examples below. @@ -99,8 +99,8 @@ basic_json(basic_json&& other) noexcept; 2. C++ has no way of describing mapped types other than to list a list of pairs. As JSON requires that keys must be of type string, rule 2 is the weakest constraint one can pose on initializer lists to interpret them as an object. - 3. In all other cases, the initializer list could not be interpreted as JSON object type, so interpreting it as JSON - array type is safe. + 3. In all other cases, the initializer list could not be interpreted as a JSON object type, so interpreting it as a + JSON array type is safe. With the rules described above, the following JSON values cannot be expressed by an initializer list: @@ -113,7 +113,7 @@ basic_json(basic_json&& other) noexcept; 6. Constructs a JSON array value by creating `cnt` copies of a passed value. In case `cnt` is `0`, an empty array is created. -7. Constructs the JSON value with the contents of the range `[first, last)`. The semantics depends on the different +7. Constructs the JSON value with the contents of the range `[first, last)`. The semantics depend on the different types a JSON value can have: - In case of a `#!json null` type, [invalid_iterator.206](../../home/exceptions.md#jsonexceptioninvalid_iterator206) @@ -175,10 +175,10 @@ basic_json(basic_json&& other) noexcept; : the number of JSON copies of `val` to create `first` (in) -: begin of the range to copy from (included) +: the beginning of the range to copy from (included) `last` (in) -: end of the range to copy from (excluded) +: the end of the range to copy from (excluded) `other` (in) : the JSON value to copy/move @@ -188,10 +188,10 @@ basic_json(basic_json&& other) noexcept; 1. Strong guarantee: if an exception is thrown, there are no changes to any JSON value. 2. No-throw guarantee: this constructor never throws exceptions. 3. Depends on the called constructor. For types directly supported by the library (i.e., all types for which no - `to_json()` function was provided), strong guarantee holds: if an exception is thrown, there are no changes to any + `to_json()` function was provided), a strong guarantee holds: if an exception is thrown, there are no changes to any JSON value. 4. Depends on the called constructor. For types directly supported by the library (i.e., all types for which no - `to_json()` function was provided), strong guarantee holds: if an exception is thrown, there are no changes to any + `to_json()` function was provided), a strong guarantee holds: if an exception is thrown, there are no changes to any JSON value. 5. Strong guarantee: if an exception is thrown, there are no changes to any JSON value. 6. Strong guarantee: if an exception is thrown, there are no changes to any JSON value. @@ -217,7 +217,7 @@ basic_json(basic_json&& other) noexcept; `[first, last)` is undefined. - Throws [`invalid_iterator.204`](../../home/exceptions.md#jsonexceptioninvalid_iterator204) if iterators `first` and `last` belong to a primitive type (number, boolean, or string), but `first` does not point to the first - element anymore. In this case, the range `[first, last)` is undefined. See example code below. + element anymore. In this case, the range `[first, last)` is undefined. See the example code below. - Throws [`invalid_iterator.206`](../../home/exceptions.md#jsonexceptioninvalid_iterator206) if iterators `first` and `last` belong to a `#!json null` value. In this case, the range `[first, last)` is undefined. 8. (none) @@ -333,7 +333,7 @@ basic_json(basic_json&& other) noexcept; --8<-- "examples/basic_json__list_init_t.output" ``` -??? example "Example: (6) construct an array with count copies of given value" +??? example "Example: (6) construct an array with count copies of a given value" The following code shows examples for creating arrays with several copies of a given value. diff --git a/docs/mkdocs/docs/api/basic_json/binary.md b/docs/mkdocs/docs/api/basic_json/binary.md index ce45d8a0fb..efd4347ca7 100644 --- a/docs/mkdocs/docs/api/basic_json/binary.md +++ b/docs/mkdocs/docs/api/basic_json/binary.md @@ -21,7 +21,7 @@ create a value for serialization to those formats. ## Parameters `init` (in) -: container containing bytes to use as binary type +: container containing bytes to use as a binary type `subtype` (in) : subtype to use in CBOR, MessagePack, and BSON @@ -42,8 +42,8 @@ Linear in the size of `init`; constant for `typename binary_t::container_type&& Note, this function exists because of the difficulty in correctly specifying the correct template overload in the standard value ctor, as both JSON arrays and JSON binary arrays are backed with some form of a `std::vector`. Because -JSON binary arrays are a non-standard extension it was decided that it would be best to prevent automatic initialization -of a binary array type, for backwards compatibility and so it does not happen on accident. +JSON binary arrays are a non-standard extension, it was decided that it would be best to prevent automatic +initialization of a binary array type, for backwards compatibility and so it does not happen on accident. ## Examples diff --git a/docs/mkdocs/docs/api/basic_json/binary_t.md b/docs/mkdocs/docs/api/basic_json/binary_t.md index 705c92cb58..902ffe732d 100644 --- a/docs/mkdocs/docs/api/basic_json/binary_t.md +++ b/docs/mkdocs/docs/api/basic_json/binary_t.md @@ -56,11 +56,11 @@ type `#!cpp binary_t*` must be dereferenced. - MessagePack - If a subtype is given and the binary array contains exactly 1, 2, 4, 8, or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8) is used. For other sizes, the ext family (ext8, ext16, ext32) is used. The subtype is - then added as signed 8-bit integer. + then added as a signed 8-bit integer. - If no subtype is given, the bin family (bin8, bin16, bin32) is used. - BSON - - If a subtype is given, it is used and added as unsigned 8-bit integer. + - If a subtype is given, it is used and added as an unsigned 8-bit integer. - If no subtype is given, the generic binary subtype 0x00 is used. ## Examples @@ -86,4 +86,4 @@ type `#!cpp binary_t*` must be dereferenced. ## Version history -- Added in version 3.8.0. Changed type of subtype to `std::uint64_t` in version 3.10.0. +- Added in version 3.8.0. Changed the type of subtype to `std::uint64_t` in version 3.10.0. diff --git a/docs/mkdocs/docs/api/basic_json/clear.md b/docs/mkdocs/docs/api/basic_json/clear.md index ff04b08e6d..427a7fc397 100644 --- a/docs/mkdocs/docs/api/basic_json/clear.md +++ b/docs/mkdocs/docs/api/basic_json/clear.md @@ -33,7 +33,7 @@ Linear in the size of the JSON value. ## Notes -All iterators, pointers and references related to this container are invalidated. +All iterators, pointers, and references related to this container are invalidated. ## Examples diff --git a/docs/mkdocs/docs/api/basic_json/contains.md b/docs/mkdocs/docs/api/basic_json/contains.md index ba2c3df2d7..41c376e573 100644 --- a/docs/mkdocs/docs/api/basic_json/contains.md +++ b/docs/mkdocs/docs/api/basic_json/contains.md @@ -35,7 +35,7 @@ bool contains(const json_pointer& ptr) const; ## Return value -1. `#!cpp true` if an element with specified `key` exists. If no such element with such key is found or the JSON value +1. `#!cpp true` if an element with specified `key` exists. If no such element with such a key is found or the JSON value is not an object, `#!cpp false` is returned. 2. See 1. 3. `#!cpp true` if the JSON pointer can be resolved to a stored value, `#!cpp false` otherwise. diff --git a/docs/mkdocs/docs/api/basic_json/dump.md b/docs/mkdocs/docs/api/basic_json/dump.md index 41adb154d6..21e22a48ce 100644 --- a/docs/mkdocs/docs/api/basic_json/dump.md +++ b/docs/mkdocs/docs/api/basic_json/dump.md @@ -8,7 +8,7 @@ string_t dump(const int indent = -1, ``` Serialization function for JSON values. The function tries to mimic Python's -[`json.dumps()` function](https://docs.python.org/2/library/json.html#json.dump), and currently supports its `indent` +[`json.dumps()` function](https://docs.python.org/2/library/json.html#json.dump), and currently supports its `indent` and `ensure_ascii` parameters. ## Parameters @@ -49,7 +49,7 @@ Linear. ## Notes -Binary values are serialized as object containing two keys: +Binary values are serialized as an object containing two keys: - "bytes": an array of bytes as integers - "subtype": the subtype as integer or `#!json null` if the binary has no subtype diff --git a/docs/mkdocs/docs/api/basic_json/emplace.md b/docs/mkdocs/docs/api/basic_json/emplace.md index 6cc2c98d75..cf637d628f 100644 --- a/docs/mkdocs/docs/api/basic_json/emplace.md +++ b/docs/mkdocs/docs/api/basic_json/emplace.md @@ -14,6 +14,11 @@ created from `args`. `Args` : compatible types to create a `basic_json` object +## Iterator invalidation + +For [`ordered_json`](../ordered_json.md), adding a value to an object can yield a reallocation, in which case all +iterators (including the `end()` iterator) and all references to the elements are invalidated. + ## Parameters `args` (in) diff --git a/docs/mkdocs/docs/api/basic_json/emplace_back.md b/docs/mkdocs/docs/api/basic_json/emplace_back.md index 597ad41e45..21829c216b 100644 --- a/docs/mkdocs/docs/api/basic_json/emplace_back.md +++ b/docs/mkdocs/docs/api/basic_json/emplace_back.md @@ -13,6 +13,12 @@ Creates a JSON value from the passed parameters `args` to the end of the JSON va `Args` : compatible types to create a `basic_json` object +## Iterator invalidation + +By adding an element to the end of the array, a reallocation can happen, in which case all iterators (including the +[`end()`](end.md) iterator) and all references to the elements are invalidated. Otherwise, only the [`end()`](end.md) +iterator is invalidated. + ## Parameters `args` (in) @@ -48,6 +54,11 @@ Amortized constant. --8<-- "examples/emplace_back.output" ``` +## See also + +- [operator+=](operator+=.md) add a value to an array/object +- [push_back](push_back.md) add a value to an array/object + ## Version history - Since version 2.0.8. diff --git a/docs/mkdocs/docs/api/basic_json/empty.md b/docs/mkdocs/docs/api/basic_json/empty.md index 26bf6e9aaa..8d566738d7 100644 --- a/docs/mkdocs/docs/api/basic_json/empty.md +++ b/docs/mkdocs/docs/api/basic_json/empty.md @@ -4,7 +4,7 @@ bool empty() const noexcept; ``` -Checks if a JSON value has no elements (i.e. whether its [`size()`](size.md) is `0`). +Checks if a JSON value has no elements (i.e., whether its [`size()`](size.md) is `0`). ## Return value diff --git a/docs/mkdocs/docs/api/basic_json/end_pos.md b/docs/mkdocs/docs/api/basic_json/end_pos.md new file mode 100644 index 0000000000..95b07297f9 --- /dev/null +++ b/docs/mkdocs/docs/api/basic_json/end_pos.md @@ -0,0 +1,68 @@ +# nlohmann::basic_json::end_pos + +```cpp +#if JSON_DIAGNOSTIC_POSITIONS +constexpr std::size_t end_pos() const noexcept; +#endif +``` + +Returns the position immediately following the last character of the JSON string from which the value was parsed from. + +| JSON type | return value | +|-----------|-----------------------------------| +| object | position after the closing `}` | +| array | position after the closing `]` | +| string | position after the closing `"` | +| number | position after the last character | +| boolean | position after `e` | +| null | position after `l` | + +## Return value + +the position of the character _following_ the last character of the given value in the parsed JSON string, if the +value was created by the [`parse`](parse.md) function, or `std::string::npos` if the value was constructed otherwise + +## Exception safety + +No-throw guarantee: this member function never throws exceptions. + +## Complexity + +Constant. + +## Notes + +!!! note "Note" + + The function is only available if macro [`JSON_DIAGNOSTIC_POSITIONS`](../macros/json_diagnostic_positions.md) has + been defined to `#!cpp 1` before including the library header. + +!!! warning "Invalidation" + + The returned positions are only valid as long as the JSON value is not changed. The positions are *not* updated + when the JSON value is changed. + +## Examples + +??? example "Example" + + ```cpp + --8<-- "examples/diagnostic_positions.cpp" + ``` + + Output: + + ``` + --8<-- "examples/diagnostic_positions.output" + ``` + + The output shows the start/end positions of all the objects and fields in the JSON string. + +## See also + +- [start_pos](start_pos.md) to access the start position +- [JSON_DIAGNOSTIC_POSITIONS](../macros/json_diagnostic_positions.md) for an overview of the diagnostic positions + +## Version history + +- Added in version 3.12.0. diff --git a/docs/mkdocs/docs/api/basic_json/exception.md b/docs/mkdocs/docs/api/basic_json/exception.md index 794b7d1e2e..b592d62eec 100644 --- a/docs/mkdocs/docs/api/basic_json/exception.md +++ b/docs/mkdocs/docs/api/basic_json/exception.md @@ -8,24 +8,36 @@ This class is an extension of [`std::exception`](https://en.cppreference.com/w/c member `id` for exception ids. It is used as the base class for all exceptions thrown by the `basic_json` class. This class can hence be used as "wildcard" to catch exceptions, see example below. -```plantuml -std::exception <|-- basic_json::exception -basic_json::exception <|-- basic_json::parse_error -basic_json::exception <|-- basic_json::invalid_iterator -basic_json::exception <|-- basic_json::type_error -basic_json::exception <|-- basic_json::out_of_range -basic_json::exception <|-- basic_json::other_error - -interface std::exception {} - -class basic_json::exception #FFFF00 { - + const int id - + const char* what() const -} - -class basic_json::parse_error { - + const std::size_t byte -} +```mermaid +classDiagram + direction LR + + class std_exception ["std::exception"] { + <> + } + + class json_exception ["basic_json::exception"] { + +const int id + +const char* what() const + } + + class json_parse_error ["basic_json::parse_error"] { + +const std::size_t byte + } + + class json_invalid_iterator ["basic_json::invalid_iterator"] + class json_type_error ["basic_json::type_error"] + class json_out_of_range ["basic_json::out_of_range"] + class json_other_error ["basic_json::other_error"] + + std_exception <|-- json_exception + json_exception <|-- json_parse_error + json_exception <|-- json_invalid_iterator + json_exception <|-- json_type_error + json_exception <|-- json_out_of_range + json_exception <|-- json_other_error + + style json_exception fill:#CCCCFF ``` Subclasses: diff --git a/docs/mkdocs/docs/api/basic_json/flatten.md b/docs/mkdocs/docs/api/basic_json/flatten.md index 8703e86d1c..7b26a89009 100644 --- a/docs/mkdocs/docs/api/basic_json/flatten.md +++ b/docs/mkdocs/docs/api/basic_json/flatten.md @@ -18,7 +18,7 @@ Strong exception safety: if an exception occurs, the original value stays intact ## Complexity -Linear in the size the JSON value. +Linear in the size of the JSON value. ## Notes diff --git a/docs/mkdocs/docs/api/basic_json/from_bjdata.md b/docs/mkdocs/docs/api/basic_json/from_bjdata.md index 3c5eeb351f..cef13aea5c 100644 --- a/docs/mkdocs/docs/api/basic_json/from_bjdata.md +++ b/docs/mkdocs/docs/api/basic_json/from_bjdata.md @@ -18,7 +18,7 @@ Deserializes a given input to a JSON value using the BJData (Binary JData) seria 1. Reads from a compatible input. 2. Reads from an iterator range. -The exact mapping and its limitations is described on a [dedicated page](../../features/binary_formats/bjdata.md). +The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/bjdata.md). ## Template parameters @@ -40,10 +40,10 @@ The exact mapping and its limitations is described on a [dedicated page](../../f : an input in BJData format convertible to an input adapter `first` (in) -: iterator to start of the input +: iterator to the start of the input `last` (in) -: iterator to end of the input +: iterator to the end of the input `strict` (in) : whether to expect the input to be consumed until EOF (`#!cpp true` by default) @@ -63,7 +63,7 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va ## Exceptions - Throws [parse_error.110](../../home/exceptions.md#jsonexceptionparse_error110) if the given input ends prematurely or - the end of file was not reached when `strict` was set to true + the end of the file was not reached when `strict` was set to true - Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if a parse error occurs - Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a string could not be parsed successfully diff --git a/docs/mkdocs/docs/api/basic_json/from_bson.md b/docs/mkdocs/docs/api/basic_json/from_bson.md index 77549370c5..fd7d0b0974 100644 --- a/docs/mkdocs/docs/api/basic_json/from_bson.md +++ b/docs/mkdocs/docs/api/basic_json/from_bson.md @@ -18,7 +18,7 @@ Deserializes a given input to a JSON value using the BSON (Binary JSON) serializ 1. Reads from a compatible input. 2. Reads from an iterator range. -The exact mapping and its limitations is described on a [dedicated page](../../features/binary_formats/bson.md). +The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/bson.md). ## Template parameters @@ -40,10 +40,10 @@ The exact mapping and its limitations is described on a [dedicated page](../../f : an input in BSON format convertible to an input adapter `first` (in) -: iterator to start of the input +: iterator to the start of the input `last` (in) -: iterator to end of the input +: iterator to the end of the input `strict` (in) : whether to expect the input to be consumed until EOF (`#!cpp true` by default) diff --git a/docs/mkdocs/docs/api/basic_json/from_cbor.md b/docs/mkdocs/docs/api/basic_json/from_cbor.md index 3aa57b9ec9..c1d1410b4c 100644 --- a/docs/mkdocs/docs/api/basic_json/from_cbor.md +++ b/docs/mkdocs/docs/api/basic_json/from_cbor.md @@ -21,7 +21,7 @@ Deserializes a given input to a JSON value using the CBOR (Concise Binary Object 1. Reads from a compatible input. 2. Reads from an iterator range. -The exact mapping and its limitations is described on a [dedicated page](../../features/binary_formats/cbor.md). +The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/cbor.md). ## Template parameters @@ -43,10 +43,10 @@ The exact mapping and its limitations is described on a [dedicated page](../../f : an input in CBOR format convertible to an input adapter `first` (in) -: iterator to start of the input +: iterator to the start of the input `last` (in) -: iterator to end of the input +: iterator to the end of the input `strict` (in) : whether to expect the input to be consumed until EOF (`#!cpp true` by default) @@ -70,10 +70,10 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va ## Exceptions - Throws [parse_error.110](../../home/exceptions.md#jsonexceptionparse_error110) if the given input ends prematurely or - the end of file was not reached when `strict` was set to true + the end of the file was not reached when `strict` was set to true - Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if unsupported features from CBOR were used in the given input or if the input is not valid CBOR -- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a string was expected as map key, +- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a string was expected as a map key, but not found ## Complexity diff --git a/docs/mkdocs/docs/api/basic_json/from_msgpack.md b/docs/mkdocs/docs/api/basic_json/from_msgpack.md index 117c3865fd..93c3acc5de 100644 --- a/docs/mkdocs/docs/api/basic_json/from_msgpack.md +++ b/docs/mkdocs/docs/api/basic_json/from_msgpack.md @@ -18,7 +18,7 @@ Deserializes a given input to a JSON value using the MessagePack serialization f 1. Reads from a compatible input. 2. Reads from an iterator range. -The exact mapping and its limitations is described on a [dedicated page](../../features/binary_formats/messagepack.md). +The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/messagepack.md). ## Template parameters @@ -40,10 +40,10 @@ The exact mapping and its limitations is described on a [dedicated page](../../f : an input in MessagePack format convertible to an input adapter `first` (in) -: iterator to start of the input +: iterator to the start of the input `last` (in) -: iterator to end of the input +: iterator to the end of the input `strict` (in) : whether to expect the input to be consumed until EOF (`#!cpp true` by default) @@ -54,7 +54,7 @@ The exact mapping and its limitations is described on a [dedicated page](../../f ## Return value deserialized JSON value; in case of a parse error and `allow_exceptions` set to `#!cpp false`, the return value will be -`value_t::discarded`. The latter can be checked with [`is_discarded`](is_discarded.md). +`value_t::discarded`. The latter can be checked with [`is_discarded`](is_discarded.md). ## Exception safety @@ -63,10 +63,10 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va ## Exceptions - Throws [parse_error.110](../../home/exceptions.md#jsonexceptionparse_error110) if the given input ends prematurely or - the end of file was not reached when `strict` was set to true + the end of the file was not reached when `strict` was set to true - Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if unsupported features from MessagePack were used in the given input or if the input is not valid MessagePack -- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a string was expected as map key, +- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a string was expected as a map key, but not found ## Complexity diff --git a/docs/mkdocs/docs/api/basic_json/from_ubjson.md b/docs/mkdocs/docs/api/basic_json/from_ubjson.md index 08117e89b4..084a2d8bba 100644 --- a/docs/mkdocs/docs/api/basic_json/from_ubjson.md +++ b/docs/mkdocs/docs/api/basic_json/from_ubjson.md @@ -18,7 +18,7 @@ Deserializes a given input to a JSON value using the UBJSON (Universal Binary JS 1. Reads from a compatible input. 2. Reads from an iterator range. -The exact mapping and its limitations is described on a [dedicated page](../../features/binary_formats/ubjson.md). +The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/ubjson.md). ## Template parameters @@ -40,10 +40,10 @@ The exact mapping and its limitations is described on a [dedicated page](../../f : an input in UBJSON format convertible to an input adapter `first` (in) -: iterator to start of the input +: iterator to the start of the input `last` (in) -: iterator to end of the input +: iterator to the end of the input `strict` (in) : whether to expect the input to be consumed until EOF (`#!cpp true` by default) @@ -63,7 +63,7 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va ## Exceptions - Throws [parse_error.110](../../home/exceptions.md#jsonexceptionparse_error110) if the given input ends prematurely or - the end of file was not reached when `strict` was set to true + the end of the file was not reached when `strict` was set to true - Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if a parse error occurs - Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a string could not be parsed successfully diff --git a/docs/mkdocs/docs/api/basic_json/front.md b/docs/mkdocs/docs/api/basic_json/front.md index e258c36a06..b5fea11352 100644 --- a/docs/mkdocs/docs/api/basic_json/front.md +++ b/docs/mkdocs/docs/api/basic_json/front.md @@ -10,8 +10,8 @@ Returns a reference to the first element in the container. For a JSON container ## Return value -In case of a structured type (array or object), a reference to the first element is returned. In case of number, string, -boolean, or binary values, a reference to the value is returned. +In the case of a structured type (array or object), a reference to the first element is returned. In the case of number, +string, boolean, or binary values, a reference to the value is returned. ## Exception safety diff --git a/docs/mkdocs/docs/api/basic_json/get_ptr.md b/docs/mkdocs/docs/api/basic_json/get_ptr.md index 2441e1156e..7c000f1ac6 100644 --- a/docs/mkdocs/docs/api/basic_json/get_ptr.md +++ b/docs/mkdocs/docs/api/basic_json/get_ptr.md @@ -35,7 +35,37 @@ Constant. !!! danger "Undefined behavior" - Writing data to the pointee of the result yields an undefined state. + The pointer becomes invalid if the underlying JSON object changes. + + Consider the following example code where the pointer `ptr` changes after the array is resized. As a result, + reading or writing to `ptr` after the array change would be undefined behavior. The address of the first array + element changes, because the underlying `std::vector` is resized after adding a fifth element. + + ```cpp + #include + #include + + using json = nlohmann::json; + + int main() + { + json j = {1, 2, 3, 4}; + auto* ptr = j[0].get_ptr(); + std::cout << "value at " << ptr << " is " << *ptr << std::endl; + + j.push_back(5); + + ptr = j[0].get_ptr(); + std::cout << "value at " << ptr << " is " << *ptr << std::endl; + } + ``` + + Output: + + ``` + value at 0x6000012fc1c8 is 1 + value at 0x6000029fc088 is 1 + ``` ## Examples @@ -54,6 +84,10 @@ Constant. --8<-- "examples/get_ptr.output" ``` +## See also + +- [get_ref()](get_ref.md) get a reference value + ## Version history - Added in version 1.0.0. diff --git a/docs/mkdocs/docs/api/basic_json/get_ref.md b/docs/mkdocs/docs/api/basic_json/get_ref.md index b1219742ca..73b20b0e08 100644 --- a/docs/mkdocs/docs/api/basic_json/get_ref.md +++ b/docs/mkdocs/docs/api/basic_json/get_ref.md @@ -40,7 +40,7 @@ Constant. !!! danger "Undefined behavior" - Writing data to the referee of the result yields an undefined state. + The reference becomes invalid if the underlying JSON object changes. ## Examples @@ -58,6 +58,10 @@ Constant. --8<-- "examples/get_ref.output" ``` +## See also + +- [get_ptr()](get_ptr.md) get a pointer value + ## Version history - Added in version 1.1.0. diff --git a/docs/mkdocs/docs/api/basic_json/index.md b/docs/mkdocs/docs/api/basic_json/index.md index 648670144e..2c942d4db3 100644 --- a/docs/mkdocs/docs/api/basic_json/index.md +++ b/docs/mkdocs/docs/api/basic_json/index.md @@ -38,11 +38,19 @@ class basic_json; ## Specializations - [**json**](../json.md) - default specialization -- [**ordered_json**](../ordered_json.md) - specialization that maintains the insertion order of object keys +- [**ordered_json**](../ordered_json.md) - a specialization that maintains the insertion order of object keys ## Iterator invalidation -Todo +All operations that add values to an **array** ([`push_back`](push_back.md) , [`operator+=`](operator+=.md), +[`emplace_back`](emplace_back.md), [`insert`](insert.md), and [`operator[]`](operator%5B%5D.md) for a non-existing +index) can yield a reallocation, in which case all iterators (including the [`end()`](end.md) iterator) and all +references to the elements are invalidated. + +For [`ordered_json`](../ordered_json.md), also all operations that add a value to an **object** +([`push_back`](push_back.md), [`operator+=`](operator+=.md), [`emplace`](emplace.md), [`insert`](insert.md), +[`update`](update.md), and [`operator[]`](operator%5B%5D.md) for a non-existing key) can yield a reallocation, in +which case all iterators (including the [`end()`](end.md) iterator) and all references to the elements are invalidated. ## Requirements @@ -50,8 +58,8 @@ The class satisfies the following concept requirements: ### Basic -- [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible): JSON values can be default - constructed. The result will be a JSON null value. +- [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible): JSON values can be + default-constructed. The result will be a JSON null value. - [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible): A JSON value can be constructed from an rvalue argument. - [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible): A JSON value can be @@ -148,9 +156,9 @@ The class satisfies the following concept requirements: - [(constructor)](basic_json.md) - [(destructor)](~basic_json.md) - [**operator=**](operator=.md) - copy assignment -- [**array**](array_t.md) (_static_) - explicitly create an array +- [**array**](array.md) (_static_) - explicitly create an array - [**binary**](binary.md) (_static_) - explicitly create a binary array -- [**object**](object_t.md) (_static_) - explicitly create an object +- [**object**](object.md) (_static_) - explicitly create an object ### Object inspection @@ -159,19 +167,24 @@ Functions to inspect the type of a JSON value. - [**type**](type.md) - return the type of the JSON value - [**operator value_t**](operator_value_t.md) - return the type of the JSON value - [**type_name**](type_name.md) - return the type as string -- [**is_primitive**](is_primitive.md) - return whether type is primitive -- [**is_structured**](is_structured.md) - return whether type is structured -- [**is_null**](is_null.md) - return whether value is null -- [**is_boolean**](is_boolean.md) - return whether value is a boolean -- [**is_number**](is_number.md) - return whether value is a number -- [**is_number_integer**](is_number_integer.md) - return whether value is an integer number -- [**is_number_unsigned**](is_number_unsigned.md) - return whether value is an unsigned integer number -- [**is_number_float**](is_number_float.md) - return whether value is a floating-point number -- [**is_object**](is_object.md) - return whether value is an object -- [**is_array**](is_array.md) - return whether value is an array -- [**is_string**](is_string.md) - return whether value is a string -- [**is_binary**](is_binary.md) - return whether value is a binary array -- [**is_discarded**](is_discarded.md) - return whether value is discarded +- [**is_primitive**](is_primitive.md) - return whether the type is primitive +- [**is_structured**](is_structured.md) - return whether the type is structured +- [**is_null**](is_null.md) - return whether the value is null +- [**is_boolean**](is_boolean.md) - return whether the value is a boolean +- [**is_number**](is_number.md) - return whether the value is a number +- [**is_number_integer**](is_number_integer.md) - return whether the value is an integer number +- [**is_number_unsigned**](is_number_unsigned.md) - return whether the value is an unsigned integer number +- [**is_number_float**](is_number_float.md) - return whether the value is a floating-point number +- [**is_object**](is_object.md) - return whether the value is an object +- [**is_array**](is_array.md) - return whether the value is an array +- [**is_string**](is_string.md) - return whether the value is a string +- [**is_binary**](is_binary.md) - return whether the value is a binary array +- [**is_discarded**](is_discarded.md) - return whether the value is discarded + +Optional functions to access the [diagnostic positions](../macros/json_diagnostic_positions.md). + +- [**start_pos**](start_pos.md) - return the start position of the value +- [**end_pos**](end_pos.md) - return the one past the end position of the value ### Value access @@ -224,7 +237,7 @@ Access to the JSON value - [**push_back**](push_back.md) - add a value to an array/object - [**operator+=**](operator+=.md) - add a value to an array/object - [**emplace_back**](emplace_back.md) - add a value to an array -- [**emplace**](emplace.md) - add a value to an object if key does not exist +- [**emplace**](emplace.md) - add a value to an object if a key does not exist - [**erase**](erase.md) - remove elements - [**insert**](insert.md) - inserts elements - [**update**](update.md) - updates a JSON object from another object, overwriting existing keys diff --git a/docs/mkdocs/docs/api/basic_json/insert.md b/docs/mkdocs/docs/api/basic_json/insert.md index 2e6b29301e..160b0d434f 100644 --- a/docs/mkdocs/docs/api/basic_json/insert.md +++ b/docs/mkdocs/docs/api/basic_json/insert.md @@ -18,11 +18,22 @@ iterator insert(const_iterator pos, initializer_list_t ilist); void insert(const_iterator first, const_iterator last); ``` -1. Inserts element `val` into array before iterator `pos`. -2. Inserts `cnt` copies of `val` into array before iterator `pos`. -3. Inserts elements from range `[first, last)` into array before iterator `pos`. -4. Inserts elements from initializer list `ilist` into array before iterator `pos`. -5. Inserts elements from range `[first, last)` into object. +1. Inserts element `val` into an array before iterator `pos`. +2. Inserts `cnt` copies of `val` into an array before iterator `pos`. +3. Inserts elements from range `[first, last)` into an array before iterator `pos`. +4. Inserts elements from initializer list `ilist` into an array before iterator `pos`. +5. Inserts elements from range `[first, last)` into an object. + +## Iterator invalidation + +For all cases where an element is added to an **array**, a reallocation can happen, in which case all iterators +(including the [`end()`](end.md) iterator) and all references to the elements are invalidated. Otherwise, only the +[`end()`](end.md) iterator is invalidated. Also, any iterator or reference after the insertion point will point to the +same index, which is now a different value. + +For [`ordered_json`](../ordered_json.md), also adding an element to an **object** can yield a reallocation which again +invalidates all iterators and all references. Also, any iterator or reference after the insertion point will point to +the same index, which is now a different value. ## Parameters @@ -36,10 +47,10 @@ void insert(const_iterator first, const_iterator last); : number of copies of `val` to insert `first` (in) -: begin of the range of elements to insert +: the start of the range of elements to insert `last` (in) -: end of the range of elements to insert +: the end of the range of elements to insert `ilist` (in) : initializer list to insert the values from @@ -128,7 +139,7 @@ Strong exception safety: if an exception occurs, the original value stays intact --8<-- "examples/insert__count.output" ``` -??? example "Example (3): insert range of elements into array" +??? example "Example (3): insert a range of elements into an array" The example shows how `insert()` is used. @@ -142,7 +153,7 @@ Strong exception safety: if an exception occurs, the original value stays intact --8<-- "examples/insert__range.output" ``` -??? example "Example (4): insert elements from initializer list into array" +??? example "Example (4): insert elements from an initializer list into an array" The example shows how `insert()` is used. @@ -156,7 +167,7 @@ Strong exception safety: if an exception occurs, the original value stays intact --8<-- "examples/insert__ilist.output" ``` -??? example "Example (5): insert range of elements into object" +??? example "Example (5): insert a range of elements into an object" The example shows how `insert()` is used. diff --git a/docs/mkdocs/docs/api/basic_json/invalid_iterator.md b/docs/mkdocs/docs/api/basic_json/invalid_iterator.md index f9fdce5b41..3f0f75356a 100644 --- a/docs/mkdocs/docs/api/basic_json/invalid_iterator.md +++ b/docs/mkdocs/docs/api/basic_json/invalid_iterator.md @@ -8,26 +8,36 @@ This exception is thrown if iterators passed to a library function do not match Exceptions have ids 2xx (see [list of iterator errors](../../home/exceptions.md#iterator-errors)). -```plantuml -std::exception <|-- basic_json::exception -basic_json::exception <|-- basic_json::parse_error -basic_json::exception <|-- basic_json::invalid_iterator -basic_json::exception <|-- basic_json::type_error -basic_json::exception <|-- basic_json::out_of_range -basic_json::exception <|-- basic_json::other_error - -interface std::exception {} - -class basic_json::exception { - + const int id - + const char* what() const -} - -class basic_json::parse_error { - + const std::size_t byte -} - -class basic_json::invalid_iterator #FFFF00 {} +```mermaid +classDiagram + direction LR + + class std_exception ["std::exception"] { + <> + } + + class json_exception ["basic_json::exception"] { + +const int id + +const char* what() const + } + + class json_parse_error ["basic_json::parse_error"] { + +const std::size_t byte + } + + class json_invalid_iterator ["basic_json::invalid_iterator"] + class json_type_error ["basic_json::type_error"] + class json_out_of_range ["basic_json::out_of_range"] + class json_other_error ["basic_json::other_error"] + + std_exception <|-- json_exception + json_exception <|-- json_parse_error + json_exception <|-- json_invalid_iterator + json_exception <|-- json_type_error + json_exception <|-- json_out_of_range + json_exception <|-- json_other_error + + style json_invalid_iterator fill:#CCCCFF ``` ## Member functions diff --git a/docs/mkdocs/docs/api/basic_json/is_binary.md b/docs/mkdocs/docs/api/basic_json/is_binary.md index ea48d745ca..2a42e5edfe 100644 --- a/docs/mkdocs/docs/api/basic_json/is_binary.md +++ b/docs/mkdocs/docs/api/basic_json/is_binary.md @@ -4,7 +4,7 @@ constexpr bool is_binary() const noexcept; ``` -This function returns `#!cpp true` if and only if the JSON value is binary array. +This function returns `#!cpp true` if and only if the JSON value is a binary array. ## Return value diff --git a/docs/mkdocs/docs/api/basic_json/is_number.md b/docs/mkdocs/docs/api/basic_json/is_number.md index 9807911bc2..2f6d63565e 100644 --- a/docs/mkdocs/docs/api/basic_json/is_number.md +++ b/docs/mkdocs/docs/api/basic_json/is_number.md @@ -9,7 +9,7 @@ unsigned) and floating-point values. ## Return value -`#!cpp true` if type is number (regardless whether integer, unsigned integer or floating-type), `#!cpp false` otherwise. +`#!cpp true` if type is number (regardless whether integer, unsigned integer, or floating-type), `#!cpp false` otherwise. ## Exception safety @@ -46,9 +46,9 @@ constexpr bool is_number() const noexcept ## See also -- [is_number_integer()](is_number_integer.md) check if value is an integer or unsigned integer number -- [is_number_unsigned()](is_number_unsigned.md) check if value is an unsigned integer number -- [is_number_float()](is_number_float.md) check if value is a floating-point number +- [is_number_integer()](is_number_integer.md) check if the value is an integer or unsigned integer number +- [is_number_unsigned()](is_number_unsigned.md) check if the value is an unsigned integer number +- [is_number_float()](is_number_float.md) check if the value is a floating-point number ## Version history diff --git a/docs/mkdocs/docs/api/basic_json/is_number_float.md b/docs/mkdocs/docs/api/basic_json/is_number_float.md index 68d0cfb01c..ad18858e3b 100644 --- a/docs/mkdocs/docs/api/basic_json/is_number_float.md +++ b/docs/mkdocs/docs/api/basic_json/is_number_float.md @@ -37,9 +37,9 @@ Constant. ## See also -- [is_number()](is_number.md) check if value is a number -- [is_number_integer()](is_number_integer.md) check if value is an integer or unsigned integer number -- [is_number_unsigned()](is_number_unsigned.md) check if value is an unsigned integer number +- [is_number()](is_number.md) check if the value is a number +- [is_number_integer()](is_number_integer.md) check if the value is an integer or unsigned integer number +- [is_number_unsigned()](is_number_unsigned.md) check if the value is an unsigned integer number ## Version history diff --git a/docs/mkdocs/docs/api/basic_json/is_number_integer.md b/docs/mkdocs/docs/api/basic_json/is_number_integer.md index 8ca214aed7..b8f971bd6c 100644 --- a/docs/mkdocs/docs/api/basic_json/is_number_integer.md +++ b/docs/mkdocs/docs/api/basic_json/is_number_integer.md @@ -37,9 +37,9 @@ Constant. ## See also -- [is_number()](is_number.md) check if value is a number -- [is_number_unsigned()](is_number_unsigned.md) check if value is an unsigned integer number -- [is_number_float()](is_number_float.md) check if value is a floating-point number +- [is_number()](is_number.md) check if the value is a number +- [is_number_unsigned()](is_number_unsigned.md) check if the value is an unsigned integer number +- [is_number_float()](is_number_float.md) check if the value is a floating-point number ## Version history diff --git a/docs/mkdocs/docs/api/basic_json/is_number_unsigned.md b/docs/mkdocs/docs/api/basic_json/is_number_unsigned.md index 2ac98a5f09..50083164ef 100644 --- a/docs/mkdocs/docs/api/basic_json/is_number_unsigned.md +++ b/docs/mkdocs/docs/api/basic_json/is_number_unsigned.md @@ -37,9 +37,9 @@ Constant. ## See also -- [is_number()](is_number.md) check if value is a number -- [is_number_integer()](is_number_integer.md) check if value is an integer or unsigned integer number -- [is_number_float()](is_number_float.md) check if value is a floating-point number +- [is_number()](is_number.md) check if the value is a number +- [is_number_integer()](is_number_integer.md) check if the value is an integer or unsigned integer number +- [is_number_float()](is_number_float.md) check if the value is a floating-point number ## Version history diff --git a/docs/mkdocs/docs/api/basic_json/is_primitive.md b/docs/mkdocs/docs/api/basic_json/is_primitive.md index cf6cbbd4c9..076c2bfef4 100644 --- a/docs/mkdocs/docs/api/basic_json/is_primitive.md +++ b/docs/mkdocs/docs/api/basic_json/is_primitive.md @@ -35,7 +35,7 @@ The term *primitive* stems from [RFC 8259](https://tools.ietf.org/html/rfc8259): > JSON can represent four primitive types (strings, numbers, booleans, and null) and two structured types (objects and > arrays). -This library extends primitive types to binary types, because binary types are roughly comparable to strings. Hence, +This library extends primitive types to binary types, because binary types are roughly comparable to strings. Hence, `is_primitive()` returns `#!cpp true` for binary values. ## Examples @@ -56,12 +56,12 @@ This library extends primitive types to binary types, because binary types are ## See also -- [is_structured()](is_structured.md) returns whether JSON value is structured -- [is_null()](is_null.md) returns whether JSON value is `null` -- [is_string()](is_string.md) returns whether JSON value is a string -- [is_boolean()](is_boolean.md) returns whether JSON value is a boolean -- [is_number()](is_number.md) returns whether JSON value is a number -- [is_binary()](is_binary.md) returns whether JSON value is a binary array +- [is_structured()](is_structured.md) returns whether the JSON value is structured +- [is_null()](is_null.md) returns whether the JSON value is `null` +- [is_string()](is_string.md) returns whether the JSON value is a string +- [is_boolean()](is_boolean.md) returns whether the JSON value is a boolean +- [is_number()](is_number.md) returns whether the JSON value is a number +- [is_binary()](is_binary.md) returns whether the JSON value is a binary array ## Version history diff --git a/docs/mkdocs/docs/api/basic_json/is_structured.md b/docs/mkdocs/docs/api/basic_json/is_structured.md index f8fe4dcbaa..f2d9e79ffe 100644 --- a/docs/mkdocs/docs/api/basic_json/is_structured.md +++ b/docs/mkdocs/docs/api/basic_json/is_structured.md @@ -21,7 +21,7 @@ Constant. ## Possible implementation ```cpp -constexpr bool is_primitive() const noexcept +constexpr bool is_structured() const noexcept { return is_array() || is_object(); } @@ -55,8 +55,8 @@ Note that though strings are containers in C++, they are treated as primitive va ## See also - [is_primitive()](is_primitive.md) returns whether JSON value is primitive -- [is_array()](is_array.md) returns whether value is an array -- [is_object()](is_object.md) returns whether value is an object +- [is_array()](is_array.md) returns whether the value is an array +- [is_object()](is_object.md) returns whether the value is an object ## Version history diff --git a/docs/mkdocs/docs/api/basic_json/items.md b/docs/mkdocs/docs/api/basic_json/items.md index 0b34ddcba3..be32bbfd9f 100644 --- a/docs/mkdocs/docs/api/basic_json/items.md +++ b/docs/mkdocs/docs/api/basic_json/items.md @@ -66,7 +66,7 @@ When iterating over an array, `key()` will return the index of the element as st !!! danger "Lifetime issues" Using `items()` on temporary objects is dangerous. Make sure the object's lifetime exceeds the iteration. See - for more information. + [#2040](https://github.com/nlohmann/json/issues/2040) for more information. ## Examples diff --git a/docs/mkdocs/docs/api/basic_json/json_base_class_t.md b/docs/mkdocs/docs/api/basic_json/json_base_class_t.md index 75752049f0..dfe5f1cb7c 100644 --- a/docs/mkdocs/docs/api/basic_json/json_base_class_t.md +++ b/docs/mkdocs/docs/api/basic_json/json_base_class_t.md @@ -16,7 +16,7 @@ Examples of such functionality might be metadata, additional member functions (e #### Default type -The default value for `CustomBaseClass` is `void`. In this case an +The default value for `CustomBaseClass` is `void`. In this case, an [empty base class](https://en.cppreference.com/w/cpp/language/ebo) is used and no additional functionality is injected. #### Limitations diff --git a/docs/mkdocs/docs/api/basic_json/number_float_t.md b/docs/mkdocs/docs/api/basic_json/number_float_t.md index 50aa43b483..3e8933da6b 100644 --- a/docs/mkdocs/docs/api/basic_json/number_float_t.md +++ b/docs/mkdocs/docs/api/basic_json/number_float_t.md @@ -13,7 +13,7 @@ The type used to store JSON numbers (floating-point). > cannot be represented in the grammar below (such as Infinity and NaN) are not permitted. This description includes both integer and floating-point numbers. However, C++ allows more precise storage if it is -known whether the number is a signed integer, an unsigned integer or a floating-point number. Therefore, three different +known whether the number is a signed integer, an unsigned integer, or a floating-point number. Therefore, three different types, [`number_integer_t`](number_integer_t.md), [`number_unsigned_t`](number_unsigned_t.md) and `number_float_t` are used. @@ -28,9 +28,9 @@ With the default values for `NumberFloatType` (`double`), the default value for #### Default behavior -- The restrictions about leading zeros is not enforced in C++. Instead, leading zeros in floating-point literals will be - ignored. Internally, the value will be stored as decimal number. For instance, the C++ floating-point literal `01.2` - will be serialized to `1.2`. During deserialization, leading zeros yield an error. +- The restrictions about leading zeros are not enforced in C++. Instead, leading zeros in floating-point literals will + be ignored. Internally, the value will be stored as a decimal number. For instance, the C++ floating-point literal + `01.2` will be serialized to `1.2`. During deserialization, leading zeros yield an error. - Not-a-number (NaN) values will be serialized to `null`. #### Limits diff --git a/docs/mkdocs/docs/api/basic_json/number_integer_t.md b/docs/mkdocs/docs/api/basic_json/number_integer_t.md index 9bb3835a0a..cef3f20010 100644 --- a/docs/mkdocs/docs/api/basic_json/number_integer_t.md +++ b/docs/mkdocs/docs/api/basic_json/number_integer_t.md @@ -13,7 +13,7 @@ The type used to store JSON numbers (integers). > cannot be represented in the grammar below (such as Infinity and NaN) are not permitted. This description includes both integer and floating-point numbers. However, C++ allows more precise storage if it is -known whether the number is a signed integer, an unsigned integer or a floating-point number. Therefore, three different +known whether the number is a signed integer, an unsigned integer, or a floating-point number. Therefore, three different types, `number_integer_t`, [`number_unsigned_t`](number_unsigned_t.md) and [`number_float_t`](number_float_t.md) are used. @@ -29,9 +29,9 @@ With the default values for `NumberIntegerType` (`std::int64_t`), the default va #### Default behavior -- The restrictions about leading zeros is not enforced in C++. Instead, leading zeros in integer literals lead to an - interpretation as octal number. Internally, the value will be stored as decimal number. For instance, the C++ integer - literal `010` will be serialized to `8`. During deserialization, leading zeros yield an error. +- The restrictions about leading zeros are not enforced in C++. Instead, leading zeros in integer literals lead to an + interpretation as an octal number. Internally, the value will be stored as a decimal number. For instance, the C++ + integer literal `010` will be serialized to `8`. During deserialization, leading zeros yield an error. - Not-a-number (NaN) values will be serialized to `null`. #### Limits @@ -42,7 +42,7 @@ With the default values for `NumberIntegerType` (`std::int64_t`), the default va When the default type is used, the maximal integer number that can be stored is `9223372036854775807` (INT64_MAX) and the minimal integer number that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers that are out of range will yield over/underflow when used in a constructor. During deserialization, too large or small integer numbers -will be automatically be stored as [`number_unsigned_t`](number_unsigned_t.md) or [`number_float_t`](number_float_t.md). +will automatically be stored as [`number_unsigned_t`](number_unsigned_t.md) or [`number_float_t`](number_float_t.md). [RFC 8259](https://tools.ietf.org/html/rfc8259) further states: > Note that when such software is used, numbers that are integers and are in the range $[-2^{53}+1, 2^{53}-1]$ are diff --git a/docs/mkdocs/docs/api/basic_json/number_unsigned_t.md b/docs/mkdocs/docs/api/basic_json/number_unsigned_t.md index 8a1540a571..79b1c486c0 100644 --- a/docs/mkdocs/docs/api/basic_json/number_unsigned_t.md +++ b/docs/mkdocs/docs/api/basic_json/number_unsigned_t.md @@ -13,7 +13,7 @@ The type used to store JSON numbers (unsigned). > cannot be represented in the grammar below (such as Infinity and NaN) are not permitted. This description includes both integer and floating-point numbers. However, C++ allows more precise storage if it is -known whether the number is a signed integer, an unsigned integer or a floating-point number. Therefore, three different +known whether the number is a signed integer, an unsigned integer, or a floating-point number. Therefore, three different types, [`number_integer_t`](number_integer_t.md), `number_unsigned_t` and [`number_float_t`](number_float_t.md) are used. @@ -29,9 +29,9 @@ With the default values for `NumberUnsignedType` (`std::uint64_t`), the default #### Default behavior -- The restrictions about leading zeros is not enforced in C++. Instead, leading zeros in integer literals lead to an - interpretation as octal number. Internally, the value will be stored as decimal number. For instance, the C++ integer - literal `010` will be serialized to `8`. During deserialization, leading zeros yield an error. +- The restrictions about leading zeros are not enforced in C++. Instead, leading zeros in integer literals lead to an + interpretation as an octal number. Internally, the value will be stored as a decimal number. For instance, the C++ + integer literal `010` will be serialized to `8`. During deserialization, leading zeros yield an error. - Not-a-number (NaN) values will be serialized to `null`. #### Limits @@ -41,7 +41,7 @@ With the default values for `NumberUnsignedType` (`std::uint64_t`), the default When the default type is used, the maximal integer number that can be stored is `18446744073709551615` (UINT64_MAX) and the minimal integer number that can be stored is `0`. Integer numbers that are out of range will yield over/underflow -when used in a constructor. During deserialization, too large or small integer numbers will be automatically be stored +when used in a constructor. During deserialization, too large or small integer numbers will automatically be stored as [`number_integer_t`](number_integer_t.md) or [`number_float_t`](number_float_t.md). [RFC 8259](https://tools.ietf.org/html/rfc8259) further states: diff --git a/docs/mkdocs/docs/api/basic_json/object.md b/docs/mkdocs/docs/api/basic_json/object.md index 9bdbddb6a1..73fbcaf790 100644 --- a/docs/mkdocs/docs/api/basic_json/object.md +++ b/docs/mkdocs/docs/api/basic_json/object.md @@ -25,7 +25,7 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va Throws [`type_error.301`](../../home/exceptions.md#jsonexceptiontype_error301) if `init` is not a list of pairs whose first elements are strings. In this case, no object can be created. When such a value is passed to `basic_json(initializer_list_t, bool, value_t)`, an array would have been created from the passed initializer list -`init`. See example below. +`init`. See the example below. ## Complexity @@ -34,7 +34,7 @@ Linear in the size of `init`. ## Notes This function is only added for symmetry reasons. In contrast to the related function `array(initializer_list_t)`, there -are no cases which can only be expressed by this function. That is, any initializer list `init` can also be passed to +are no cases that can only be expressed by this function. That is, any initializer list `init` can also be passed to the initializer list constructor `basic_json(initializer_list_t, bool, value_t)`. ## Examples diff --git a/docs/mkdocs/docs/api/basic_json/object_t.md b/docs/mkdocs/docs/api/basic_json/object_t.md index 39f68b0891..7dda42d2bf 100644 --- a/docs/mkdocs/docs/api/basic_json/object_t.md +++ b/docs/mkdocs/docs/api/basic_json/object_t.md @@ -87,8 +87,8 @@ Objects are stored as pointers in a `basic_json` type. That is, for any access t #### Object key order -The order name/value pairs are added to the object is *not* preserved by the library. Therefore, iterating an object may -return name/value pairs in a different order than they were originally stored. In fact, keys will be traversed in +The order name/value pairs are added to the object are *not* preserved by the library. Therefore, iterating an object +may return name/value pairs in a different order than they were originally stored. In fact, keys will be traversed in alphabetical order as `std::map` with `std::less` is used by default. Please note this behavior conforms to [RFC 8259](https://tools.ietf.org/html/rfc8259), because any order implements the specified "unordered" nature of JSON objects. diff --git a/docs/mkdocs/docs/api/basic_json/operator+=.md b/docs/mkdocs/docs/api/basic_json/operator+=.md index dc5f2ecc44..473b82a4e2 100644 --- a/docs/mkdocs/docs/api/basic_json/operator+=.md +++ b/docs/mkdocs/docs/api/basic_json/operator+=.md @@ -27,6 +27,15 @@ reference operator+=(initializer_list_t init); `init` is converted into an object element and added using `operator+=(const typename object_t::value_type&)`. Otherwise, `init` is converted to a JSON value and added using `operator+=(basic_json&&)`. +## Iterator invalidation + +For all cases where an element is added to an **array**, a reallocation can happen, in which case all iterators (including +the [`end()`](end.md) iterator) and all references to the elements are invalidated. Otherwise, only the +[`end()`](end.md) iterator is invalidated. + +For [`ordered_json`](../ordered_json.md), also adding an element to an **object** can yield a reallocation which again +invalidates all iterators and all references. + ## Parameters `val` (in) @@ -103,8 +112,13 @@ interpreted as `object_t::value_type` or `std::initializer_list`, se --8<-- "examples/push_back__initializer_list.output" ``` +## See also + +- [emplace_back](emplace_back.md) add a value to an array +- [push_back](push_back.md) add a value to an array/object + ## Version history 1. Since version 1.0.0. 2. Since version 1.0.0. -2. Since version 2.0.0. +3. Since version 2.0.0. diff --git a/docs/mkdocs/docs/api/basic_json/operator[].md b/docs/mkdocs/docs/api/basic_json/operator[].md index 51dd8588cb..3eebd562a7 100644 --- a/docs/mkdocs/docs/api/basic_json/operator[].md +++ b/docs/mkdocs/docs/api/basic_json/operator[].md @@ -34,6 +34,15 @@ const_reference operator[](const json_pointer& ptr) const; [`string_t`](string_t.md) using [`object_comparator_t`](object_comparator_t.md). This can also be a string view (C++17). +## Iterator invalidation + +For the non-const versions 1. and 4., when passing an **array** index that does not exist, it is created and filled with +a `#!json null` value before a reference to it is returned. For this, a reallocation can happen, in which case all +iterators (including the [`end()`](end.md) iterator) and all references to the elements are invalidated. + +For [`ordered_json`](../ordered_json.md), also passing an **object key** to the non-const versions 2., 3., and 4., a +reallocation can happen which again invalidates all iterators and all references. + ## Parameters `idx` (in) diff --git a/docs/mkdocs/docs/api/basic_json/operator_le.md b/docs/mkdocs/docs/api/basic_json/operator_le.md index c6144bb452..9dfa4e1e08 100644 --- a/docs/mkdocs/docs/api/basic_json/operator_le.md +++ b/docs/mkdocs/docs/api/basic_json/operator_le.md @@ -17,7 +17,7 @@ bool operator<=(ScalarType lhs, const const_reference rhs) noexcept; // (2) operand is `NaN` and the other operand is either `NaN` or any other number. - Otherwise, returns the result of `#!cpp !(rhs < lhs)` (see [**operator<**](operator_lt.md)). -1. Compares whether a JSON value is less than or equal to a scalar or a scalar is less than or equal +2. Compares whether a JSON value is less than or equal to a scalar or a scalar is less than or equal to a JSON value by converting the scalar to a JSON value and comparing both JSON values according to 1. diff --git a/docs/mkdocs/docs/api/basic_json/other_error.md b/docs/mkdocs/docs/api/basic_json/other_error.md index 9a83340a04..251c0f3d44 100644 --- a/docs/mkdocs/docs/api/basic_json/other_error.md +++ b/docs/mkdocs/docs/api/basic_json/other_error.md @@ -8,26 +8,36 @@ This exception is thrown in case of errors that cannot be classified with the ot Exceptions have ids 5xx (see [list of other errors](../../home/exceptions.md#further-exceptions)). -```plantuml -std::exception <|-- basic_json::exception -basic_json::exception <|-- basic_json::parse_error -basic_json::exception <|-- basic_json::invalid_iterator -basic_json::exception <|-- basic_json::type_error -basic_json::exception <|-- basic_json::out_of_range -basic_json::exception <|-- basic_json::other_error - -interface std::exception {} - -class basic_json::exception { - + const int id - + const char* what() const -} - -class basic_json::parse_error { - + const std::size_t byte -} - -class basic_json::other_error #FFFF00 {} +```mermaid +classDiagram + direction LR + + class std_exception ["std::exception"] { + <> + } + + class json_exception ["basic_json::exception"] { + +const int id + +const char* what() const + } + + class json_parse_error ["basic_json::parse_error"] { + +const std::size_t byte + } + + class json_invalid_iterator ["basic_json::invalid_iterator"] + class json_type_error ["basic_json::type_error"] + class json_out_of_range ["basic_json::out_of_range"] + class json_other_error ["basic_json::other_error"] + + std_exception <|-- json_exception + json_exception <|-- json_parse_error + json_exception <|-- json_invalid_iterator + json_exception <|-- json_type_error + json_exception <|-- json_out_of_range + json_exception <|-- json_other_error + + style json_other_error fill:#CCCCFF ``` ## Member functions diff --git a/docs/mkdocs/docs/api/basic_json/out_of_range.md b/docs/mkdocs/docs/api/basic_json/out_of_range.md index 6c1f0dfbab..3b61789995 100644 --- a/docs/mkdocs/docs/api/basic_json/out_of_range.md +++ b/docs/mkdocs/docs/api/basic_json/out_of_range.md @@ -5,30 +5,40 @@ class out_of_range : public exception; ``` This exception is thrown in case a library function is called on an input parameter that exceeds the expected range, for -instance in case of array indices or nonexisting object keys. +instance, in the case of array indices or nonexisting object keys. Exceptions have ids 4xx (see [list of out-of-range errors](../../home/exceptions.md#out-of-range)). -```plantuml -std::exception <|-- basic_json::exception -basic_json::exception <|-- basic_json::parse_error -basic_json::exception <|-- basic_json::invalid_iterator -basic_json::exception <|-- basic_json::type_error -basic_json::exception <|-- basic_json::out_of_range -basic_json::exception <|-- basic_json::other_error - -interface std::exception {} - -class basic_json::exception { - + const int id - + const char* what() const -} - -class basic_json::parse_error { - + const std::size_t byte -} - -class basic_json::out_of_range #FFFF00 {} +```mermaid +classDiagram + direction LR + + class std_exception ["std::exception"] { + <> + } + + class json_exception ["basic_json::exception"] { + +const int id + +const char* what() const + } + + class json_parse_error ["basic_json::parse_error"] { + +const std::size_t byte + } + + class json_invalid_iterator ["basic_json::invalid_iterator"] + class json_type_error ["basic_json::type_error"] + class json_out_of_range ["basic_json::out_of_range"] + class json_other_error ["basic_json::other_error"] + + std_exception <|-- json_exception + json_exception <|-- json_parse_error + json_exception <|-- json_invalid_iterator + json_exception <|-- json_type_error + json_exception <|-- json_out_of_range + json_exception <|-- json_other_error + + style json_out_of_range fill:#CCCCFF ``` ## Member functions diff --git a/docs/mkdocs/docs/api/basic_json/parse.md b/docs/mkdocs/docs/api/basic_json/parse.md index 49838ad1d4..7baa16b894 100644 --- a/docs/mkdocs/docs/api/basic_json/parse.md +++ b/docs/mkdocs/docs/api/basic_json/parse.md @@ -6,21 +6,23 @@ template 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 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. 2. Deserialize from a pair of character iterators - The `value_type` of the iterator must be an integral type with size of 1, 2 or 4 bytes, which will be interpreted - respectively as UTF-8, UTF-16 and UTF-32. + The `value_type` of the iterator must be an integral type with size of 1, 2, or 4 bytes, which will be interpreted + respectively as UTF-8, UTF-16, and UTF-32. ## Template parameters @@ -28,9 +30,9 @@ static basic_json parse(IteratorType first, IteratorType last, : A compatible input, for instance: - an `std::istream` object - - a `FILE` pointer (must not be null) + - a `FILE` pointer (throws if null) - a C-style array of characters - - a pointer to a null-terminated string of single byte characters + - a pointer to a null-terminated string of single byte characters (throws if null) - a `std::string` - an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of iterators. @@ -56,11 +58,15 @@ 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 start of character range +: iterator to the start of a character range `last` (in) -: iterator to end of character range +: iterator to the end of a character range ## Return value @@ -73,10 +79,11 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va ## Exceptions -- Throws [`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101) in case of an unexpected token. -- Throws [`parse_error.102`](../../home/exceptions.md#jsonexceptionparse_error102) if to_unicode fails or surrogate +- Throws [`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101) in case of an unexpected token, or + empty input like a null `FILE*` or `char*` pointer. +- Throws [`parse_error.102`](../../home/exceptions.md#jsonexceptionparse_error102) if `to_unicode` fails or surrogate error. -- Throws [`parse_error.103`](../../home/exceptions.md#jsonexceptionparse_error103) if to_unicode fails. +- Throws [`parse_error.103`](../../home/exceptions.md#jsonexceptionparse_error103) if `to_unicode` fails. ## Complexity @@ -86,12 +93,7 @@ super-linear complexity. ## Notes -(1) A UTF-8 byte order mark is silently ignored. - -!!! danger "Runtime assertion" - - The precondition that a passed `#!cpp FILE` pointer must not be null is enforced with a - [runtime assertion](../../features/assertions.md). +A UTF-8 byte order mark is silently ignored. ## Examples @@ -151,7 +153,7 @@ super-linear complexity. --8<-- "examples/parse__contiguouscontainer__parser_callback_t.output" ``` -??? example "Parsing from a non null-terminated string" +??? example "Parsing from a non-null-terminated string" The example below demonstrates the `parse()` function reading from a string that is not null-terminated. @@ -193,6 +195,34 @@ super-linear complexity. --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 @@ -203,6 +233,8 @@ super-linear complexity. - Added in version 1.0.0. - 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" diff --git a/docs/mkdocs/docs/api/basic_json/parse_error.md b/docs/mkdocs/docs/api/basic_json/parse_error.md index af3e1f0b36..93986e5439 100644 --- a/docs/mkdocs/docs/api/basic_json/parse_error.md +++ b/docs/mkdocs/docs/api/basic_json/parse_error.md @@ -4,31 +4,43 @@ class parse_error : public exception; ``` -This exception is thrown by the library when a parse error occurs. Parse errors can occur during the deserialization of +The library throws this exception when a parse error occurs. Parse errors can occur during the deserialization of JSON text, BSON, CBOR, MessagePack, UBJSON, as well as when using JSON Patch. Member `byte` holds the byte index of the last read character in the input file (see note below). Exceptions have ids 1xx (see [list of parse errors](../../home/exceptions.md#parse-errors)). -```plantuml -std::exception <|-- basic_json::exception -basic_json::exception <|-- basic_json::parse_error -basic_json::exception <|-- basic_json::invalid_iterator -basic_json::exception <|-- basic_json::type_error -basic_json::exception <|-- basic_json::out_of_range -basic_json::exception <|-- basic_json::other_error - -interface std::exception {} - -class basic_json::exception { - + const int id - + const char* what() const -} - -class basic_json::parse_error #FFFF00 { - + const std::size_t byte -} +```mermaid +classDiagram + direction LR + + class std_exception ["std::exception"] { + <> + } + + class json_exception ["basic_json::exception"] { + +const int id + +const char* what() const + } + + class json_parse_error ["basic_json::parse_error"] { + +const std::size_t byte + } + + class json_invalid_iterator ["basic_json::invalid_iterator"] + class json_type_error ["basic_json::type_error"] + class json_out_of_range ["basic_json::out_of_range"] + class json_other_error ["basic_json::other_error"] + + std_exception <|-- json_exception + json_exception <|-- json_parse_error + json_exception <|-- json_invalid_iterator + json_exception <|-- json_type_error + json_exception <|-- json_out_of_range + json_exception <|-- json_other_error + + style json_parse_error fill:#CCCCFF ``` ## Member functions diff --git a/docs/mkdocs/docs/api/basic_json/patch.md b/docs/mkdocs/docs/api/basic_json/patch.md index deec47434c..0f6387a69a 100644 --- a/docs/mkdocs/docs/api/basic_json/patch.md +++ b/docs/mkdocs/docs/api/basic_json/patch.md @@ -37,8 +37,8 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va ## Complexity -Linear in the size of the JSON value and the length of the JSON patch. As usually only a fraction of the JSON value is -affected by the patch, the complexity can usually be neglected. +Linear in the size of the JSON value and the length of the JSON patch. As usually the patch affects only a fraction of +the JSON value, the complexity can usually be neglected. ## Notes diff --git a/docs/mkdocs/docs/api/basic_json/patch_inplace.md b/docs/mkdocs/docs/api/basic_json/patch_inplace.md index e8fd176c3a..6cc13e1027 100644 --- a/docs/mkdocs/docs/api/basic_json/patch_inplace.md +++ b/docs/mkdocs/docs/api/basic_json/patch_inplace.md @@ -33,8 +33,8 @@ No guarantees, value may be corrupted by an unsuccessful patch operation. ## Complexity -Linear in the size of the JSON value and the length of the JSON patch. As usually only a fraction of the JSON value is -affected by the patch, the complexity can usually be neglected. +Linear in the size of the JSON value and the length of the JSON patch. As usually the patch affects only a fraction of +the JSON value, the complexity can usually be neglected. ## Notes diff --git a/docs/mkdocs/docs/api/basic_json/push_back.md b/docs/mkdocs/docs/api/basic_json/push_back.md index 5c7d20dd6a..49f185c56c 100644 --- a/docs/mkdocs/docs/api/basic_json/push_back.md +++ b/docs/mkdocs/docs/api/basic_json/push_back.md @@ -27,6 +27,15 @@ void push_back(initializer_list_t init); `init` is converted into an object element and added using `push_back(const typename object_t::value_type&)`. Otherwise, `init` is converted to a JSON value and added using `push_back(basic_json&&)`. +## Iterator invalidation + +For all cases where an element is added to an **array**, a reallocation can happen, in which case all iterators (including +the [`end()`](end.md) iterator) and all references to the elements are invalidated. Otherwise, only the +[`end()`](end.md) iterator is invalidated. + +For [`ordered_json`](../ordered_json.md), also adding an element to an **object** can yield a reallocation which again +invalidates all iterators and all references. + ## Parameters `val` (in) @@ -99,8 +108,13 @@ All functions can throw the following exception: --8<-- "examples/push_back__initializer_list.output" ``` +## See also + +- [emplace_back](emplace_back.md) add a value to an array +- [operator+=](operator+=.md) add a value to an array/object + ## Version history 1. Since version 1.0.0. 2. Since version 1.0.0. -2. Since version 2.0.0. +3. Since version 2.0.0. diff --git a/docs/mkdocs/docs/api/basic_json/sax_parse.md b/docs/mkdocs/docs/api/basic_json/sax_parse.md index e2ac1b41d9..530c6f85f6 100644 --- a/docs/mkdocs/docs/api/basic_json/sax_parse.md +++ b/docs/mkdocs/docs/api/basic_json/sax_parse.md @@ -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 @@ -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 @@ -23,8 +25,8 @@ Read from input and generate SAX events 1. Read from a compatible input. 2. Read from a pair of character iterators - The value_type of the iterator must be an integral type with size of 1, 2 or 4 bytes, which will be interpreted - respectively as UTF-8, UTF-16 and UTF-32. + The value_type of the iterator must be an integral type with a size of 1, 2, or 4 bytes, which will be interpreted + respectively as UTF-8, UTF-16, and UTF-32. The SAX event lister must follow the interface of [`json_sax`](../json_sax/index.md). @@ -49,10 +51,10 @@ The SAX event lister must follow the interface of [`json_sax`](../json_sax/index ## Parameters `i` (in) -: Input to parse from. +: Input to parse from `sax` (in) -: SAX event listener +: SAX event listener (must not be null) `format` (in) : the format to parse (JSON, CBOR, MessagePack, or UBJSON) (optional, `input_format_t::json` by default), see @@ -65,11 +67,15 @@ 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 start of character range +: iterator to the start of a character range `last` (in) -: iterator to end of character range +: iterator to the end of a character range ## Return value @@ -77,6 +83,17 @@ return value of the last processed SAX event ## Exception safety +Strong guarantee: if an exception is thrown, there are no changes in the JSON value. + +## Exceptions + +- Throws [`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101) in case of an unexpected token, or + empty input like a null `FILE*` or `char*` pointer. +- Throws [`parse_error.102`](../../home/exceptions.md#jsonexceptionparse_error102) if `to_unicode` fails or surrogate + error. +- Throws [`parse_error.103`](../../home/exceptions.md#jsonexceptionparse_error103) if `to_unicode` fails. +- Throws [`other_error.502`](../../home/exceptions.md#jsonexceptionother_error502) if `sax` is a null pointer. + ## Complexity Linear in the length of the input. The parser is a predictive LL(1) parser. The complexity can be higher if the SAX @@ -107,6 +124,8 @@ 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. +- Added `json.exception.other_error.502` exception in version 3.12.1. !!! warning "Deprecation" diff --git a/docs/mkdocs/docs/api/basic_json/start_pos.md b/docs/mkdocs/docs/api/basic_json/start_pos.md new file mode 100644 index 0000000000..19a31df15e --- /dev/null +++ b/docs/mkdocs/docs/api/basic_json/start_pos.md @@ -0,0 +1,68 @@ +# nlohmann::basic_json::start_pos + +```cpp +#if JSON_DIAGNOSTIC_POSITIONS +constexpr std::size_t start_pos() const noexcept; +#endif +``` + +Returns the position of the first character in the JSON string from which the value was parsed from. + +| JSON type | return value | +|-----------|------------------------------------------------| +| object | position of the opening `{` | +| array | position of the opening `[` | +| string | position of the opening `"` | +| number | position of the first character | +| boolean | position of `t` for `true` and `f` for `false` | +| null | position of `n` | + +## Return value + +the position of the first character of the value in the parsed JSON string, if the value was created by the +[`parse`](parse.md) function, or `std::string::npos` if the value was constructed otherwise + +## Exception safety + +No-throw guarantee: this member function never throws exceptions. + +## Complexity + +Constant. + +## Notes + +!!! note "Note" + + The function is only available if macro [`JSON_DIAGNOSTIC_POSITIONS`](../macros/json_diagnostic_positions.md) has + been defined to `#!cpp 1` before including the library header. + +!!! warning "Invalidation" + + The returned positions are only valid as long as the JSON value is not changed. The positions are *not* updated + when the JSON value is changed. + +## Examples + +??? example "Example" + + ```cpp + --8<-- "examples/diagnostic_positions.cpp" + ``` + + Output: + + ``` + --8<-- "examples/diagnostic_positions.output" + ``` + + The output shows the start/end positions of all the objects and fields in the JSON string. + +## See also + +- [end_pos](end_pos.md) to access the end position +- [JSON_DIAGNOSTIC_POSITIONS](../macros/json_diagnostic_positions.md) for an overview of the diagnostic positions + +## Version history + +- Added in version 3.12.0. diff --git a/docs/mkdocs/docs/api/basic_json/to_bjdata.md b/docs/mkdocs/docs/api/basic_json/to_bjdata.md index 48598a5e61..7aa331d023 100644 --- a/docs/mkdocs/docs/api/basic_json/to_bjdata.md +++ b/docs/mkdocs/docs/api/basic_json/to_bjdata.md @@ -4,13 +4,16 @@ // (1) static std::vector to_bjdata(const basic_json& j, const bool use_size = false, - const bool use_type = false); + const bool use_type = false, + const bjdata_version_t version = bjdata_version_t::draft2); // (2) static void to_bjdata(const basic_json& j, detail::output_adapter o, - const bool use_size = false, const bool use_type = false); + const bool use_size = false, const bool use_type = false, + const bjdata_version_t version = bjdata_version_t::draft2); static void to_bjdata(const basic_json& j, detail::output_adapter o, - const bool use_size = false, const bool use_type = false); + const bool use_size = false, const bool use_type = false, + const bjdata_version_t version = bjdata_version_t::draft2); ``` Serializes a given JSON value `j` to a byte vector using the BJData (Binary JData) serialization format. BJData aims to @@ -19,7 +22,7 @@ be more compact than JSON itself, yet more efficient to parse. 1. Returns a byte vector containing the BJData serialization. 2. Writes the BJData serialization to an output adapter. -The exact mapping and its limitations is described on a [dedicated page](../../features/binary_formats/bjdata.md). +The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/bjdata.md). ## Parameters @@ -36,6 +39,10 @@ The exact mapping and its limitations is described on a [dedicated page](../../f : whether to add type annotations to container types (must be combined with `#!cpp use_size = true`); optional, `#!cpp false` by default. +`version` (in) +: which version of BJData to use (see note on "Binary values" on [BJData](../../features/binary_formats/bjdata.md)); +optional, `#!cpp bjdata_version_t::draft2` by default. + ## Return value 1. BJData serialization as byte vector @@ -68,3 +75,4 @@ Linear in the size of the JSON value `j`. ## Version history - Added in version 3.11.0. +- BJData version parameter (for draft3 binary encoding) added in version 3.12.0. \ No newline at end of file diff --git a/docs/mkdocs/docs/api/basic_json/to_bson.md b/docs/mkdocs/docs/api/basic_json/to_bson.md index 5c4324a3fd..d0cae0ea32 100644 --- a/docs/mkdocs/docs/api/basic_json/to_bson.md +++ b/docs/mkdocs/docs/api/basic_json/to_bson.md @@ -15,7 +15,7 @@ so-called document). 1. Returns a byte vector containing the BSON serialization. 2. Writes the BSON serialization to an output adapter. -The exact mapping and its limitations is described on a [dedicated page](../../features/binary_formats/bson.md). +The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/bson.md). ## Parameters @@ -27,7 +27,7 @@ The exact mapping and its limitations is described on a [dedicated page](../../f ## Return value -1. BSON serialization as byte vector +1. BSON serialization as a byte vector 2. (none) ## Exception safety diff --git a/docs/mkdocs/docs/api/basic_json/to_cbor.md b/docs/mkdocs/docs/api/basic_json/to_cbor.md index 0f944c4810..81d66467bc 100644 --- a/docs/mkdocs/docs/api/basic_json/to_cbor.md +++ b/docs/mkdocs/docs/api/basic_json/to_cbor.md @@ -10,13 +10,13 @@ static void to_cbor(const basic_json& j, detail::output_adapter o); ``` Serializes a given JSON value `j` to a byte vector using the CBOR (Concise Binary Object Representation) serialization -format. CBOR is a binary serialization format which aims to be more compact than JSON itself, yet more efficient to +format. CBOR is a binary serialization format that aims to be more compact than JSON itself, yet more efficient to parse. 1. Returns a byte vector containing the CBOR serialization. 2. Writes the CBOR serialization to an output adapter. -The exact mapping and its limitations is described on a [dedicated page](../../features/binary_formats/cbor.md). +The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/cbor.md). ## Parameters @@ -28,7 +28,7 @@ The exact mapping and its limitations is described on a [dedicated page](../../f ## Return value -1. CBOR serialization as byte vector +1. CBOR serialization as a byte vector 2. (none) ## Exception safety diff --git a/docs/mkdocs/docs/api/basic_json/to_msgpack.md b/docs/mkdocs/docs/api/basic_json/to_msgpack.md index 7d40981d5f..3ae43a0494 100644 --- a/docs/mkdocs/docs/api/basic_json/to_msgpack.md +++ b/docs/mkdocs/docs/api/basic_json/to_msgpack.md @@ -10,12 +10,12 @@ static void to_msgpack(const basic_json& j, detail::output_adapter o); ``` Serializes a given JSON value `j` to a byte vector using the MessagePack serialization format. MessagePack is a binary -serialization format which aims to be more compact than JSON itself, yet more efficient to parse. +serialization format that aims to be more compact than JSON itself, yet more efficient to parse. 1. Returns a byte vector containing the MessagePack serialization. 2. Writes the MessagePack serialization to an output adapter. -The exact mapping and its limitations is described on a [dedicated page](../../features/binary_formats/messagepack.md). +The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/messagepack.md). ## Parameters @@ -27,7 +27,7 @@ The exact mapping and its limitations is described on a [dedicated page](../../f ## Return value -1. MessagePack serialization as byte vector +1. MessagePack serialization as a byte vector 2. (none) ## Exception safety diff --git a/docs/mkdocs/docs/api/basic_json/to_ubjson.md b/docs/mkdocs/docs/api/basic_json/to_ubjson.md index e3cd5d62b9..a272803147 100644 --- a/docs/mkdocs/docs/api/basic_json/to_ubjson.md +++ b/docs/mkdocs/docs/api/basic_json/to_ubjson.md @@ -19,7 +19,7 @@ aims to be more compact than JSON itself, yet more efficient to parse. 1. Returns a byte vector containing the UBJSON serialization. 2. Writes the UBJSON serialization to an output adapter. -The exact mapping and its limitations is described on a [dedicated page](../../features/binary_formats/ubjson.md). +The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/ubjson.md). ## Parameters @@ -38,7 +38,7 @@ The exact mapping and its limitations is described on a [dedicated page](../../f ## Return value -1. UBJSON serialization as byte vector +1. UBJSON serialization as a byte vector 2. (none) ## Exception safety diff --git a/docs/mkdocs/docs/api/basic_json/type_error.md b/docs/mkdocs/docs/api/basic_json/type_error.md index cda54c089c..4d590556f4 100644 --- a/docs/mkdocs/docs/api/basic_json/type_error.md +++ b/docs/mkdocs/docs/api/basic_json/type_error.md @@ -9,26 +9,36 @@ does not match the expected semantics. Exceptions have ids 3xx (see [list of type errors](../../home/exceptions.md#type-errors)). -```plantuml -std::exception <|-- basic_json::exception -basic_json::exception <|-- basic_json::parse_error -basic_json::exception <|-- basic_json::invalid_iterator -basic_json::exception <|-- basic_json::type_error -basic_json::exception <|-- basic_json::out_of_range -basic_json::exception <|-- basic_json::other_error - -interface std::exception {} - -class basic_json::exception { - + const int id - + const char* what() const -} - -class basic_json::parse_error { - + const std::size_t byte -} - -class basic_json::type_error #FFFF00 {} +```mermaid +classDiagram + direction LR + + class std_exception ["std::exception"] { + <> + } + + class json_exception ["basic_json::exception"] { + +const int id + +const char* what() const + } + + class json_parse_error ["basic_json::parse_error"] { + +const std::size_t byte + } + + class json_invalid_iterator ["basic_json::invalid_iterator"] + class json_type_error ["basic_json::type_error"] + class json_out_of_range ["basic_json::out_of_range"] + class json_other_error ["basic_json::other_error"] + + std_exception <|-- json_exception + json_exception <|-- json_parse_error + json_exception <|-- json_invalid_iterator + json_exception <|-- json_type_error + json_exception <|-- json_out_of_range + json_exception <|-- json_other_error + + style json_type_error fill:#CCCCFF ``` ## Member functions diff --git a/docs/mkdocs/docs/api/basic_json/unflatten.md b/docs/mkdocs/docs/api/basic_json/unflatten.md index d9778036c2..5585cbc31c 100644 --- a/docs/mkdocs/docs/api/basic_json/unflatten.md +++ b/docs/mkdocs/docs/api/basic_json/unflatten.md @@ -28,11 +28,11 @@ The function can throw the following exceptions: ## Complexity -Linear in the size the JSON value. +Linear in the size of the JSON value. ## Notes -Empty objects and arrays are flattened by [`flatten()`](flatten.md) to `#!json null` values and can not unflattened to +Empty objects and arrays are flattened by [`flatten()`](flatten.md) to `#!json null` values and cannot unflattened to their original type. Apart from this example, for a JSON value `j`, the following is always true: `#!cpp j == j.flatten().unflatten()`. diff --git a/docs/mkdocs/docs/api/basic_json/update.md b/docs/mkdocs/docs/api/basic_json/update.md index f3546157a7..267a705173 100644 --- a/docs/mkdocs/docs/api/basic_json/update.md +++ b/docs/mkdocs/docs/api/basic_json/update.md @@ -17,6 +17,11 @@ recursively merges objects with common keys. The function is motivated by Python's [dict.update](https://docs.python.org/3.6/library/stdtypes.html#dict.update) function. +## Iterator invalidation + +For [`ordered_json`](../ordered_json.md), adding a value to an object can yield a reallocation, in which case all +iterators (including the `end()` iterator) and all references to the elements are invalidated. + ## Parameters `j` (in) @@ -27,10 +32,10 @@ function. `#!c false`) `first` (in) -: begin of the range of elements to insert +: the beginning of the range of elements to insert `last` (in) -: end of the range of elements to insert +: the end of the range of elements to insert ## Exceptions diff --git a/docs/mkdocs/docs/api/basic_json/value.md b/docs/mkdocs/docs/api/basic_json/value.md index edb5406ba6..c9bca6be74 100644 --- a/docs/mkdocs/docs/api/basic_json/value.md +++ b/docs/mkdocs/docs/api/basic_json/value.md @@ -103,6 +103,30 @@ changes to any JSON value. 2. Logarithmic in the size of the container. 3. Logarithmic in the size of the container. +## Notes + +!!! warning "Return type" + + The value function is a template, and the return type of the function is determined by the type of the provided + default value unless otherwise specified. This can have unexpected effects. In the example below, we store a 64-bit + unsigned integer. We get exactly that value when using [`operator[]`](operator[].md). However, when we call `value` + and provide `#!c 0` as default value, then `#!c -1` is returned. The occurs, because `#!c 0` has type `#!c int` + which overflows when handling the value `#!c 18446744073709551615`. + + To address this issue, either provide a correctly typed default value or use the template parameter to specify the + desired return type. Note that this issue occurs even when a value is stored at the provided key, and the default + value is not used as the return value. + + ```cpp + --8<-- "examples/value__return_type.cpp" + ``` + + Output: + + ```json + --8<-- "examples/value__return_type.output" + ``` + ## Examples ??? example "Example: (1) access specified object element with default value" diff --git a/docs/mkdocs/docs/api/byte_container_with_subtype/byte_container_with_subtype.md b/docs/mkdocs/docs/api/byte_container_with_subtype/byte_container_with_subtype.md index 9913a9b5c9..c8e47cfa34 100644 --- a/docs/mkdocs/docs/api/byte_container_with_subtype/byte_container_with_subtype.md +++ b/docs/mkdocs/docs/api/byte_container_with_subtype/byte_container_with_subtype.md @@ -13,9 +13,9 @@ byte_container_with_subtype(const container_type& container, subtype_type subtyp byte_container_with_subtype(container_type&& container, subtype_type subtype); ``` -1. Create empty binary container without subtype. -2. Create binary container without subtype. -3. Create binary container with subtype. +1. Create an empty binary container without a subtype. +2. Create a binary container without a subtype. +3. Create a binary container with a subtype. ## Parameters diff --git a/docs/mkdocs/docs/api/byte_container_with_subtype/clear_subtype.md b/docs/mkdocs/docs/api/byte_container_with_subtype/clear_subtype.md index c62dead36b..f4bb891ee8 100644 --- a/docs/mkdocs/docs/api/byte_container_with_subtype/clear_subtype.md +++ b/docs/mkdocs/docs/api/byte_container_with_subtype/clear_subtype.md @@ -5,7 +5,7 @@ void clear_subtype() noexcept; ``` Clears the binary subtype and flags the value as not having a subtype, which has implications for serialization; for -instance MessagePack will prefer the bin family over the ext family. +instance, MessagePack will prefer the bin family over the ext family. ## Exception safety diff --git a/docs/mkdocs/docs/api/byte_container_with_subtype/index.md b/docs/mkdocs/docs/api/byte_container_with_subtype/index.md index 277fffa345..85c09079b7 100644 --- a/docs/mkdocs/docs/api/byte_container_with_subtype/index.md +++ b/docs/mkdocs/docs/api/byte_container_with_subtype/index.md @@ -7,7 +7,7 @@ class byte_container_with_subtype : public BinaryType; This type extends the template parameter `BinaryType` provided to [`basic_json`](../basic_json/index.md) with a subtype used by BSON and MessagePack. This type exists so that the user does not have to specify a type themselves with a -specific naming scheme in order to override the binary type. +specific naming scheme in order to override the binary type. ## Template parameters @@ -32,4 +32,4 @@ specific naming scheme in order to override the binary type. ## Version history - Added in version 3.8.0. -- Changed type of subtypes to `#!cpp std::uint64_t` in 3.10.0. +- Changed the type of subtypes to `#!cpp std::uint64_t` in 3.10.0. diff --git a/docs/mkdocs/docs/api/json_pointer/back.md b/docs/mkdocs/docs/api/json_pointer/back.md index 240bc6e1e7..7b798e368f 100644 --- a/docs/mkdocs/docs/api/json_pointer/back.md +++ b/docs/mkdocs/docs/api/json_pointer/back.md @@ -4,7 +4,7 @@ const string_t& back() const; ``` -Return last reference token. +Return the last reference token. ## Return value @@ -12,7 +12,7 @@ Last reference token. ## Exceptions -Throws [out_of_range.405](../../home/exceptions.md#jsonexceptionout_of_range405) if JSON pointer has no parent. +Throws [out_of_range.405](../../home/exceptions.md#jsonexceptionout_of_range405) if the JSON pointer has no parent. ## Complexity diff --git a/docs/mkdocs/docs/api/json_pointer/empty.md b/docs/mkdocs/docs/api/json_pointer/empty.md index 346364ad50..96328bd23c 100644 --- a/docs/mkdocs/docs/api/json_pointer/empty.md +++ b/docs/mkdocs/docs/api/json_pointer/empty.md @@ -4,7 +4,7 @@ bool empty() const noexcept; ``` -Return whether pointer points to the root document. +Return whether the pointer points to the root document. ## Return value diff --git a/docs/mkdocs/docs/api/json_pointer/index.md b/docs/mkdocs/docs/api/json_pointer/index.md index 22e2464053..8b70e2de71 100644 --- a/docs/mkdocs/docs/api/json_pointer/index.md +++ b/docs/mkdocs/docs/api/json_pointer/index.md @@ -34,10 +34,10 @@ are the base for JSON patches. - [**operator/=**](operator_slasheq.md) - append to the end of the JSON pointer - [**operator/**](operator_slash.md) - create JSON Pointer by appending - [**parent_pointer**](parent_pointer.md) - returns the parent of this JSON pointer -- [**pop_back**](pop_back.md) - remove last reference token +- [**pop_back**](pop_back.md) - remove the last reference token - [**back**](back.md) - return last reference token - [**push_back**](push_back.md) - append an unescaped token at the end of the pointer -- [**empty**](empty.md) - return whether pointer points to the root document +- [**empty**](empty.md) - return whether the pointer points to the root document ## Literals diff --git a/docs/mkdocs/docs/api/json_pointer/operator_string_t.md b/docs/mkdocs/docs/api/json_pointer/operator_string_t.md index 74105a4f1e..89898fa4f6 100644 --- a/docs/mkdocs/docs/api/json_pointer/operator_string_t.md +++ b/docs/mkdocs/docs/api/json_pointer/operator_string_t.md @@ -42,6 +42,10 @@ operator string_t() const --8<-- "examples/json_pointer__operator_string_t.output" ``` +## See also + +- [string_t](../basic_json/string_t.md)- type for strings + ## Version history - Since version 2.0.0. diff --git a/docs/mkdocs/docs/api/json_pointer/pop_back.md b/docs/mkdocs/docs/api/json_pointer/pop_back.md index 3c79f36383..16b1cd4dad 100644 --- a/docs/mkdocs/docs/api/json_pointer/pop_back.md +++ b/docs/mkdocs/docs/api/json_pointer/pop_back.md @@ -4,11 +4,11 @@ void pop_back(); ``` -Remove last reference token. +Remove the last reference token. ## Exceptions -Throws [out_of_range.405](../../home/exceptions.md#jsonexceptionout_of_range405) if JSON pointer has no parent. +Throws [out_of_range.405](../../home/exceptions.md#jsonexceptionout_of_range405) if the JSON pointer has no parent. ## Complexity diff --git a/docs/mkdocs/docs/api/json_sax/index.md b/docs/mkdocs/docs/api/json_sax/index.md index f63e85c9a6..d66b7a254b 100644 --- a/docs/mkdocs/docs/api/json_sax/index.md +++ b/docs/mkdocs/docs/api/json_sax/index.md @@ -17,7 +17,7 @@ processing the input. ## Member types - [**number_integer_t**](../basic_json/number_integer_t.md) - `BasicJsonType`'s type for numbers (integer) -- [**number_unsigned_t**](../basic_json/number_unsigned_t.md) - `BasicJsonType`'s type for numbers (unsigned) +- [**number_unsigned_t**](../basic_json/number_unsigned_t.md) - `BasicJsonType`'s type for numbers (unsigned) - [**number_float_t**](../basic_json/number_float_t.md) - `BasicJsonType`'s type for numbers (floating-point) - [**string_t**](../basic_json/string_t.md) - `BasicJsonType`'s type for strings - [**binary_t**](../basic_json/binary_t.md) - `BasicJsonType`'s type for binary arrays diff --git a/docs/mkdocs/docs/api/macros/index.md b/docs/mkdocs/docs/api/macros/index.md index ae9eb20443..59e4b3d280 100644 --- a/docs/mkdocs/docs/api/macros/index.md +++ b/docs/mkdocs/docs/api/macros/index.md @@ -11,6 +11,7 @@ header. See also the [macro overview page](../../features/macros.md). - [**JSON_CATCH_USER(exception)**
**JSON_THROW_USER(exception)**
**JSON_TRY_USER**](json_throw_user.md) - control exceptions - [**JSON_DIAGNOSTICS**](json_diagnostics.md) - control extended diagnostics +- [**JSON_DIAGNOSTIC_POSITIONS**](json_diagnostic_positions.md) - access positions of elements - [**JSON_NOEXCEPTION**](json_noexception.md) - switch off exceptions ## Language support @@ -42,7 +43,6 @@ header. See also the [macro overview page](../../features/macros.md). - [**JSON_DISABLE_ENUM_SERIALIZATION**](json_disable_enum_serialization.md) - switch off default serialization/deserialization functions for enums - [**JSON_USE_IMPLICIT_CONVERSIONS**](json_use_implicit_conversions.md) - control implicit conversions - ## Comparison behavior - [**JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON**](json_use_legacy_discarded_value_comparison.md) - @@ -50,13 +50,34 @@ header. See also the [macro overview page](../../features/macros.md). ## Serialization/deserialization macros -- [**NLOHMANN_DEFINE_TYPE_INTRUSIVE(type, member...)**
**NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(type, member...)** -
**NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(type, member...)**][DefInt] - \- serialization/deserialization of types _with_ access to private variables -- [**NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(type, member...)**
**NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(type, member...)** -
**NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(type, member...)**][DefNonInt] - \- serialization/deserialization of types _without_ access to private variables -- [**NLOHMANN_JSON_SERIALIZE_ENUM(type, ...)**](nlohmann_json_serialize_enum.md) - serialization/deserialization of enum types - -[DefInt]: nlohmann_define_type_intrusive.md -[DefNonInt]: nlohmann_define_type_non_intrusive.md +### Enums + +- [**NLOHMANN_JSON_SERIALIZE_ENUM**](nlohmann_json_serialize_enum.md) - serialize/deserialize an enum + +### Classes and structs + +- [**NLOHMANN_DEFINE_TYPE_INTRUSIVE**](nlohmann_define_type_intrusive.md) - serialize/deserialize a non-derived class + with private members +- [**NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT**](nlohmann_define_type_intrusive.md) - serialize/deserialize a + non-derived class with private members; uses default values +- [**NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE**](nlohmann_define_type_intrusive.md) - serialize a non-derived class + with private members +- [**NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE**](nlohmann_define_type_non_intrusive.md) - serialize/deserialize a non-derived + class +- [**NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT**](nlohmann_define_type_non_intrusive.md) - serialize/deserialize a + non-derived class; uses default values +- [**NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE**](nlohmann_define_type_non_intrusive.md) - serialize a + non-derived class + +- [**NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE**](nlohmann_define_derived_type.md) - serialize/deserialize a derived class + with private members +- [**NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT**](nlohmann_define_derived_type.md) - serialize/deserialize a + derived class with private members; uses default values +- [**NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE**](nlohmann_define_derived_type.md) - serialize a derived + class with private members +- [**NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE**](nlohmann_define_derived_type.md) - serialize/deserialize a derived + class +- [**NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT**](nlohmann_define_derived_type.md) - serialize/deserialize + a derived class; uses default values +- [**NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE**](nlohmann_define_derived_type.md) - serialize a derived + class diff --git a/docs/mkdocs/docs/api/macros/json_assert.md b/docs/mkdocs/docs/api/macros/json_assert.md index a093341a1b..2d7b0c78a1 100644 --- a/docs/mkdocs/docs/api/macros/json_assert.md +++ b/docs/mkdocs/docs/api/macros/json_assert.md @@ -9,7 +9,7 @@ This macro controls which code is executed for [runtime assertions](../../featur ## Parameters `x` (in) -: expression of scalar type +: expression of a scalar type ## Default definition @@ -79,6 +79,10 @@ Therefore, assertions can be switched off by defining `NDEBUG`. assertion error in operator[] ``` +## See also + +- [Runtime Assertions](../../features/assertions.md) - overview documentation + ## Version history - Added in version 3.9.0. diff --git a/docs/mkdocs/docs/api/macros/json_diagnostic_positions.md b/docs/mkdocs/docs/api/macros/json_diagnostic_positions.md new file mode 100644 index 0000000000..f928f227ea --- /dev/null +++ b/docs/mkdocs/docs/api/macros/json_diagnostic_positions.md @@ -0,0 +1,119 @@ +# JSON_DIAGNOSTIC_POSITIONS + +```cpp +#define JSON_DIAGNOSTIC_POSITIONS /* value */ +``` + +This macro enables position diagnostics for generated JSON objects. + +When enabled, two new member functions [`start_pos()`](../basic_json/start_pos.md) and +[`end_pos()`](../basic_json/end_pos.md) are added to [`basic_json`](../basic_json/index.md) values. If the value was +created by calling the[`parse`](../basic_json/parse.md) function, then these functions allow querying the byte positions +of the value in the input it was parsed from. In case the value was constructed by other means, `std::string::npos` is +returned. + +[`start_pos()`](../basic_json/start_pos.md) returns the position of the first character of a given value in the original +JSON string, while [`end_pos()`](../basic_json/end_pos.md) returns the position of the character _following_ the last +character. For objects and arrays, the first and last characters correspond to the opening or closing braces/brackets, +respectively. For primitive values, the first and last character represents the opening and closing quotes (strings) or +the first and last character of the field's numerical or predefined value (`true`, `false`, `null`), respectively. + +| JSON type | return value [`start_pos()`](../basic_json/start_pos.md) | return value [`end_pos()`](../basic_json/end_pos.md) | +|-----------|----------------------------------------------------------|------------------------------------------------------| +| object | position of the opening `{` | position after the closing `}` | +| array | position of the opening `[` | position after the closing `]` | +| string | position of the opening `"` | position after the closing `"` | +| number | position of the first character | position after the last character | +| boolean | position of `t` for `true` and `f` for `false` | position after `e` | +| null | position of `n` | position after `l` | + +Given the above, [`end_pos()`](../basic_json/end_pos.md)` - `[`start_pos()`](../basic_json/start_pos.md) for a JSON +value provides the length of the parsed JSON string for that value, including the opening or closing braces, brackets, +or quotes. + +Note that enabling this macro increases the size of every JSON value by two `std::size_t` fields and adds slight runtime +overhead to parsing, copying JSON value objects, and the generation of error messages for exceptions. It also causes +these values to be reported in those error messages. + +## Default definition + +The default value is `0` (position diagnostics are switched off). + +```cpp +#define JSON_DIAGNOSTIC_POSITIONS 0 +``` + +When the macro is not defined, the library will define it to its default value. + +## Notes + +!!! note "CMake option" + + Diagnostic positions can also be controlled with the CMake option + [`JSON_Diagnostic_Positions`](../../integration/cmake.md#json_diagnostic_positions) (`OFF` by default) + which defines `JSON_DIAGNOSTIC_POSITIONS` accordingly. + +!!! note "Availability" + + Diagnostic positions are only available if the value was created by the [`parse`](../basic_json/parse.md) function. + The [`sax_parse`](../basic_json/sax_parse.md) function or all other means to create a JSON value **do not** set the + diagnostic positions and [`start_pos()`](../basic_json/start_pos.md) and [`end_pos()`](../basic_json/end_pos.md) + will only return `std::string::npos` for these values. + +!!! warning "Invalidation" + + The returned positions are only valid as long as the JSON value is not changed. The positions are *not* updated + when the JSON value is changed. + +## Examples + +??? example "Example: retrieving positions" + + ```cpp + --8<-- "examples/diagnostic_positions.cpp" + ``` + + Output: + + ``` + --8<-- "examples/diagnostic_positions.output" + ``` + + The output shows the start/end positions of all the objects and fields in the JSON string. + +??? example "Example 2: using only diagnostic positions in exceptions" + + ```cpp + --8<-- "examples/diagnostic_positions_exception.cpp" + ``` + + Output: + + ``` + --8<-- "examples/diagnostic_positions_exception.output" + ``` + + The output shows the exception with start/end positions only. + +??? example "Example 3: using extended diagnostics with positions enabled in exceptions" + + ```cpp + --8<-- "examples/diagnostics_extended_positions.cpp" + ``` + + Output: + + ``` + --8<-- "examples/diagnostics_extended_positions.output" + ``` + + The output shows the exception with diagnostic path info and start/end positions. + +## See also + +- [:simple-cmake: JSON_Diagnostic_Positions](../../integration/cmake.md#json_diagnostic_positions) - CMake option to control the macro +- [JSON_DIAGNOSTICS](json_diagnostics.md) - macro to control extended diagnostics + +## Version history + +- Added in version 3.12.0. diff --git a/docs/mkdocs/docs/api/macros/json_diagnostics.md b/docs/mkdocs/docs/api/macros/json_diagnostics.md index 4fc0fc38e3..91bdc1b39e 100644 --- a/docs/mkdocs/docs/api/macros/json_diagnostics.md +++ b/docs/mkdocs/docs/api/macros/json_diagnostics.md @@ -9,7 +9,7 @@ Possible values are `1` to enable or `0` to disable (default). When enabled, exception messages contain a [JSON Pointer](../json_pointer/json_pointer.md) to the JSON value that triggered the exception. Note that enabling this macro increases the size of every JSON value by one pointer and adds -some runtime overhead. +some runtime overhead. ## Default definition @@ -70,7 +70,25 @@ When the macro is not defined, the library will define it to its default value. Now the exception message contains a JSON Pointer `/address/housenumber` that indicates which value has the wrong type. +??? example "Example 3: using only diagnostic positions in exceptions" + + ```cpp + --8<-- "examples/diagnostic_positions_exception.cpp" + ``` + + Output: + + ``` + --8<-- "examples/diagnostic_positions_exception.output" + ``` + The output shows the exception with start/end positions only. + +## See also + +- [:simple-cmake: JSON_Diagnostics](../../integration/cmake.md#json_diagnostics) - CMake option to control the macro +- [JSON_DIAGNOSTIC_POSITIONS](json_diagnostic_positions.md) - macro to access positions of elements + ## Version history - Added in version 3.10.0. -- As of version 3.11.0 the definition is allowed to vary between translation units. +- As of version 3.11.0, the definition is allowed to vary between translation units. diff --git a/docs/mkdocs/docs/api/macros/json_disable_enum_serialization.md b/docs/mkdocs/docs/api/macros/json_disable_enum_serialization.md index 6440e34e32..e1418943d1 100644 --- a/docs/mkdocs/docs/api/macros/json_disable_enum_serialization.md +++ b/docs/mkdocs/docs/api/macros/json_disable_enum_serialization.md @@ -53,7 +53,7 @@ The default value is `0`. const json j = Choice::first; // normally invokes from_json parse function but with JSON_DISABLE_ENUM_SERIALIZATION defined, it does not - Choice ch = j.template get(); + Choice ch = j.get(); } ``` @@ -86,7 +86,7 @@ The default value is `0`. const json j = Choice::first; // uses user-defined from_json function defined by macro - Choice ch = j.template get(); + Choice ch = j.get(); } ``` @@ -109,7 +109,7 @@ The default value is `0`. void from_json(const json& j, Choice& ch) { - auto value = j.template get(); + auto value = j.get(); if (value == "first") { ch = Choice::first; @@ -122,7 +122,7 @@ The default value is `0`. void to_json(json& j, const Choice& ch) { - auto value = j.template get(); + auto value = j.get(); if (value == "first") { ch = Choice::first; @@ -139,13 +139,15 @@ The default value is `0`. const json j = Choice::first; // uses user-defined from_json function - Choice ch = j.template get(); + Choice ch = j.get(); } ``` ## See also -- [`NLOHMANN_JSON_SERIALIZE_ENUM`](nlohmann_json_serialize_enum.md) +- [:simple-cmake: JSON_DisableEnumSerialization](../../integration/cmake.md#json_disableenumserialization) - CMake option to control + the macro +- [`NLOHMANN_JSON_SERIALIZE_ENUM`](nlohmann_json_serialize_enum.md) - serialize/deserialize an enum ## Version history diff --git a/docs/mkdocs/docs/api/macros/json_skip_unsupported_compiler_check.md b/docs/mkdocs/docs/api/macros/json_skip_unsupported_compiler_check.md index 374fa4c27f..52cdbd1727 100644 --- a/docs/mkdocs/docs/api/macros/json_skip_unsupported_compiler_check.md +++ b/docs/mkdocs/docs/api/macros/json_skip_unsupported_compiler_check.md @@ -4,8 +4,8 @@ #define JSON_SKIP_UNSUPPORTED_COMPILER_CHECK ``` -When defined, the library will not create a compile error when a known unsupported compiler is detected. This allows to -use the library with compilers that do not fully support C++11 and may only work if unsupported features are not used. +When defined, the library will not create a compile error when a known unsupported compiler is detected. This allows +using the library with compilers that do not fully support C++11 and may only work if unsupported features are not used. ## Default definition diff --git a/docs/mkdocs/docs/api/macros/json_use_global_udls.md b/docs/mkdocs/docs/api/macros/json_use_global_udls.md index 69db9e77c3..3110d4662d 100644 --- a/docs/mkdocs/docs/api/macros/json_use_global_udls.md +++ b/docs/mkdocs/docs/api/macros/json_use_global_udls.md @@ -92,6 +92,7 @@ When the macro is not defined, the library will define it to its default value. - [`operator""_json`](../operator_literal_json.md) - [`operator""_json_pointer`](../operator_literal_json_pointer.md) +- [:simple-cmake: JSON_GlobalUDLs](../../integration/cmake.md#json_globaludls) - CMake option to control the macro ## Version history diff --git a/docs/mkdocs/docs/api/macros/json_use_implicit_conversions.md b/docs/mkdocs/docs/api/macros/json_use_implicit_conversions.md index 557dfa299e..22f6d00725 100644 --- a/docs/mkdocs/docs/api/macros/json_use_implicit_conversions.md +++ b/docs/mkdocs/docs/api/macros/json_use_implicit_conversions.md @@ -27,7 +27,7 @@ By default, implicit conversions are enabled. !!! hint "CMake option" Implicit conversions can also be controlled with the CMake option - [`JSON_ImplicitConversions`](../../integration/cmake.md#json_legacydiscardedvaluecomparison) + [`JSON_ImplicitConversions`](../../integration/cmake.md#json_implicitconversions) (`ON` by default) which defines `JSON_USE_IMPLICIT_CONVERSIONS` accordingly. ## Examples @@ -46,13 +46,14 @@ By default, implicit conversions are enabled. ```cpp json j = "Hello, world!"; - auto s = j.template get(); + auto s = j.get(); ``` ## See also - [**operator ValueType**](../basic_json/operator_ValueType.md) - get a value (implicit) - [**get**](../basic_json/get.md) - get a value (explicit) +- [:simple-cmake: JSON_ImplicitConversions](../../integration/cmake.md#json_implicitconversions) - CMake option to control the macro ## Version history diff --git a/docs/mkdocs/docs/api/macros/json_use_legacy_discarded_value_comparison.md b/docs/mkdocs/docs/api/macros/json_use_legacy_discarded_value_comparison.md index bc1d1434a7..b14266304a 100644 --- a/docs/mkdocs/docs/api/macros/json_use_legacy_discarded_value_comparison.md +++ b/docs/mkdocs/docs/api/macros/json_use_legacy_discarded_value_comparison.md @@ -56,7 +56,7 @@ When the macro is not defined, the library will define it to its default value. !!! hint "CMake option" Legacy comparison can also be controlled with the CMake option - [`JSON_LegacyDiscardedValueComparison`](../../integration/cmake.md#json_legacydiscardedvaluecomparison) + [`JSON_LegacyDiscardedValueComparison`](../../integration/cmake.md#json_implicitconversions) (`OFF` by default) which defines `JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON` accordingly. ## Examples @@ -72,6 +72,10 @@ When the macro is not defined, the library will define it to its default value. ... ``` +### See also + +- [:simple-cmake: JSON_LegacyDiscardedValueComparison](../../integration/cmake.md#json_legacydiscardedvaluecomparison) - CMake option to control the macro + ## Version history - Added in version 3.11.0. diff --git a/docs/mkdocs/docs/api/macros/nlohmann_define_derived_type.md b/docs/mkdocs/docs/api/macros/nlohmann_define_derived_type.md new file mode 100644 index 0000000000..d33bc1eebd --- /dev/null +++ b/docs/mkdocs/docs/api/macros/nlohmann_define_derived_type.md @@ -0,0 +1,177 @@ +

NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE, NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT, + NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE, + NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE

+ +```cpp +// (1) +#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(type, base_type, member...) +// (2) +#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT(type, base_type, member...) +// (3) +#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE(type, base_type, member...) + +// (4) +#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(type, base_type, member...) +// (5) +#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT(type, base_type, member...) +// (6) +#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(type, base_type, member...) +``` + +These macros can be used to simplify the serialization/deserialization of derived types if you want to use a JSON +object as serialization and want to use the member variable names as object keys in that object. + +- Macros 1, 2, and 3 are to be defined **inside** the class/struct to create code for. +Like [`NLOHMANN_DEFINE_TYPE_INTRUSIVE`](nlohmann_define_type_intrusive.md), they can access private members. +- Macros 4, 5, and 6 are to be defined **outside** the class/struct to create code for, but **inside** its namespace. +Like [`NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`](nlohmann_define_type_non_intrusive.md), +they **cannot** access private members. + +The first parameter is the name of the derived class/struct, +the second parameter is the name of the base class/struct and all remaining parameters name the members. +The base type **must** be already serializable/deserializable. + +- Macros 1 and 4 will use [`at`](../basic_json/at.md) during deserialization and will throw + [`out_of_range.403`](../../home/exceptions.md#jsonexceptionout_of_range403) if a key is missing in the JSON object. +- Macros 2 and 5 will use [`value`](../basic_json/value.md) during deserialization and fall back to the default value for the + respective type of the member variable if a key in the JSON object is missing. The generated `from_json()` function + default constructs an object and uses its values as the defaults when calling the `value` function. + +Summary: + +| Need access to private members | Need only de-serialization | Allow missing values when de-serializing | macro | +|------------------------------------------------------------------|------------------------------------------------------------------|------------------------------------------------------------------|---------------------------------------------------------------| +|
:octicons-check-circle-fill-24:
|
:octicons-x-circle-fill-24:
|
:octicons-x-circle-fill-24:
| **NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE** | +|
:octicons-check-circle-fill-24:
|
:octicons-x-circle-fill-24:
|
:octicons-check-circle-fill-24:
| **NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT** | +|
:octicons-check-circle-fill-24:
|
:octicons-check-circle-fill-24:
|
:octicons-skip-fill-24:
| **NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE** | +|
:octicons-x-circle-fill-24:
|
:octicons-x-circle-fill-24:
|
:octicons-x-circle-fill-24:
| **NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE** | +|
:octicons-x-circle-fill-24:
|
:octicons-x-circle-fill-24:
|
:octicons-check-circle-fill-24:
| **NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT** | +|
:octicons-x-circle-fill-24:
|
:octicons-check-circle-fill-24:
|
:octicons-skip-fill-24:
| **NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE** | + +## Parameters + +`type` (in) +: name of the type (class, struct) to serialize/deserialize + +`base_type` (in) +: name of the base type (class, struct) `type` is derived from + +`member` (in) +: name of the member variable to serialize/deserialize; up to 64 members can be given as a comma-separated list + +## Default definition + +Macros 1 and 2 add two friend functions to the class which take care of the serialization and deserialization: + +```cpp +template +friend void to_json(BasicJsonType&, const type&); +template +friend void from_json(const BasicJsonType&, type&); +``` + +Macros 4 and 5 add two functions to the namespace which take care of the serialization and deserialization: + +```cpp +template +void to_json(BasicJsonType&, const type&); +template +void from_json(const BasicJsonType&, type&); +``` + +Macros 3 and 6 add one function to the namespace, which takes care of the serialization only: + +```cpp +template +void to_json(BasicJsonType&, const type&); +``` + +In first two cases, they call the `to_json`/`from_json` functions of the base type +before serializing/deserializing the members of the derived type: + +```cpp +class A { /* ... */ }; +class B : public A { /* ... */ }; + +template +void to_json(BasicJsonType& j, const B& b) { + nlohmann::to_json(j, static_cast(b)); + // ... +} + +template +void from_json(const BasicJsonType& j, B& b) { + nlohmann::from_json(j, static_cast(b)); + // ... +} +``` + +In the third case, only `to_json` will be called: + +```cpp +class A { /* ... */ }; +class B : public A { /* ... */ }; + +template +void to_json(BasicJsonType& j, const B& b) { + nlohmann::to_json(j, static_cast(b)); + // ... +} +``` + +## Notes + +!!! info "Prerequisites" + + - Macros 1, 2, and 3 have the same prerequisites of [NLOHMANN_DEFINE_TYPE_INTRUSIVE](nlohmann_define_type_intrusive.md). + - Macros 4, 5, and 6 have the same prerequisites of [NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE](nlohmann_define_type_non_intrusive.md). + - Serialization/deserialization of base types must be defined. + +!!! warning "Implementation limits" + + See Implementation limits for [NLOHMANN_DEFINE_TYPE_INTRUSIVE](nlohmann_define_type_intrusive.md) and + [NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE](nlohmann_define_type_non_intrusive.md), respectively. + +## Examples + +??? example "NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE" + + Consider the following complete example: + + ```cpp hl_lines="28" + --8<-- "examples/nlohmann_define_derived_type_intrusive_macro.cpp" + ``` + + Output: + + ```json + --8<-- "examples/nlohmann_define_derived_type_intrusive_macro.output" + ``` + + Notes: + + - `A` and `B` are default-constructible. This is a requirement for using the macro. + - `A` has private members and is not a derived class. Hence, macro `NLOHMANN_DEFINE_TYPE_INTRUSIVE` is used. + - As `B` is a derived class, `NLOHMANN_DEFINE_TYPE_INTRUSIVE` is not applicable, but + `NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE` must be used. + - The macro `NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE` is used _inside_ the class use as + `NLOHMANN_DEFINE_TYPE_INTRUSIVE`. + +## See also + +- [NLOHMANN_DEFINE_TYPE_INTRUSIVE / NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT / + NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE](nlohmann_define_type_intrusive.md) + for similar macros that can be defined _inside_ a non-derived type. +- [NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE / NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT / + NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE](nlohmann_define_type_non_intrusive.md) + for similar macros that can be defined _outside_ a non-derived type. +- [Arbitrary Type Conversions](../../features/arbitrary_types.md) for an overview. + +## Version history + +1. Added in version 3.12.0. +2. Added in version 3.12.0. +3. Added in version 3.12.0. +4. Added in version 3.12.0. +5. Added in version 3.12.0. +6. Added in version 3.12.0. diff --git a/docs/mkdocs/docs/api/macros/nlohmann_define_type_intrusive.md b/docs/mkdocs/docs/api/macros/nlohmann_define_type_intrusive.md index ad425810af..b95d26799b 100644 --- a/docs/mkdocs/docs/api/macros/nlohmann_define_type_intrusive.md +++ b/docs/mkdocs/docs/api/macros/nlohmann_define_type_intrusive.md @@ -17,7 +17,15 @@ parameter is the name of the class/struct, and all remaining parameters name the 2. Will use [`value`](../basic_json/value.md) during deserialization and fall back to the default value for the respective type of the member variable if a key in the JSON object is missing. The generated `from_json()` function default constructs an object and uses its values as the defaults when calling the `value` function. -3. Only defines the serialization. Useful in cases when the type does not have a default constructor and only serialization in required. +3. Only defines the serialization. Useful in cases when the type does not have a default constructor and only serialization is required. + +Summary: + +| Need access to private members | Need only de-serialization | Allow missing values when de-serializing | macro | +|------------------------------------------------------------------|------------------------------------------------------------------|------------------------------------------------------------------|-------------------------------------------------------| +|
:octicons-check-circle-fill-24:
|
:octicons-x-circle-fill-24:
|
:octicons-x-circle-fill-24:
| **NLOHMANN_DEFINE_TYPE_INTRUSIVE** | +|
:octicons-check-circle-fill-24:
|
:octicons-x-circle-fill-24:
|
:octicons-check-circle-fill-24:
| **NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT** | +|
:octicons-check-circle-fill-24:
|
:octicons-check-circle-fill-24:
|
:octicons-skip-fill-24:
| **NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE** | ## Parameters @@ -25,18 +33,20 @@ parameter is the name of the class/struct, and all remaining parameters name the : name of the type (class, struct) to serialize/deserialize `member` (in) -: name of the member variable to serialize/deserialize; up to 64 members can be given as comma-separated list +: name of the member variable to serialize/deserialize; up to 64 members can be given as a comma-separated list ## Default definition The macros add two friend functions to the class which take care of the serialization and deserialization: ```cpp -friend void to_json(nlohmann::json&, const type&); -friend void from_json(const nlohmann::json&, type&); // except (3) +template +friend void to_json(BasicJsonType&, const type&); +template +friend void from_json(const BasicJsonType&, type&); // except (3) ``` -See examples below for the concrete generated code. +See the examples below for the concrete generated code. ## Notes @@ -52,8 +62,6 @@ See examples below for the concrete generated code. - The current implementation is limited to at most 64 member variables. If you want to serialize/deserialize types with more than 64 member variables, you need to define the `to_json`/`from_json` functions manually. - - The macros only work for the [`nlohmann::json`](../json.md) type; other specializations such as - [`nlohmann::ordered_json`](../ordered_json.md) are currently unsupported. ## Examples @@ -82,7 +90,7 @@ See examples below for the concrete generated code. The macro is equivalent to: - ```cpp hl_lines="22 23 24 25 26 27 28 29 30 31 32 33 34" + ```cpp hl_lines="22 23 24 25 26 27 28 29 30 31 32 33 34 35 36" --8<-- "examples/nlohmann_define_type_intrusive_explicit.cpp" ``` @@ -110,7 +118,7 @@ See examples below for the concrete generated code. The macro is equivalent to: - ```cpp hl_lines="22 23 24 25 26 27 28 29 30 31 32 33 34 35" + ```cpp hl_lines="22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37" --8<-- "examples/nlohmann_define_type_intrusive_with_default_explicit.cpp" ``` @@ -139,18 +147,24 @@ See examples below for the concrete generated code. The macro is equivalent to: - ```cpp hl_lines="22 22 23 24 25 26 27" + ```cpp hl_lines="22 22 23 24 25 26 27 28" --8<-- "examples/nlohmann_define_type_intrusive_only_serialize_explicit.cpp" ``` ## See also -- [NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE{_WITH_DEFAULT, _ONLY_SERIALIZE}](nlohmann_define_type_non_intrusive.md) +- [NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE, NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT, + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE](nlohmann_define_type_non_intrusive.md) for a similar macro that can be defined _outside_ the type. +- [NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE, NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT, + NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE, + NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT, + NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE](nlohmann_define_derived_type.md) for similar macros for + derived types - [Arbitrary Type Conversions](../../features/arbitrary_types.md) for an overview. ## Version history 1. Added in version 3.9.0. 2. Added in version 3.11.0. -3. Added in version TODO. +3. Added in version 3.11.3. diff --git a/docs/mkdocs/docs/api/macros/nlohmann_define_type_non_intrusive.md b/docs/mkdocs/docs/api/macros/nlohmann_define_type_non_intrusive.md index 5830f8ca9f..c3e2721c0d 100644 --- a/docs/mkdocs/docs/api/macros/nlohmann_define_type_non_intrusive.md +++ b/docs/mkdocs/docs/api/macros/nlohmann_define_type_non_intrusive.md @@ -17,7 +17,15 @@ parameter is the name of the class/struct, and all remaining parameters name the 2. Will use [`value`](../basic_json/value.md) during deserialization and fall back to the default value for the respective type of the member variable if a key in the JSON object is missing. The generated `from_json()` function default constructs an object and uses its values as the defaults when calling the `value` function. -3. Only defines the serialization. Useful in cases when the type does not have a default constructor and only serialization in required. +3. Only defines the serialization. Useful in cases when the type does not have a default constructor and only serialization is required. + +Summary: + +| Need access to private members | Need only de-serialization | Allow missing values when de-serializing | macro | +|------------------------------------------------------------------|------------------------------------------------------------------|------------------------------------------------------------------|-------------------------------------------------------| +|
:octicons-x-circle-fill-24:
|
:octicons-x-circle-fill-24:
|
:octicons-x-circle-fill-24:
| **NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE** | +|
:octicons-x-circle-fill-24:
|
:octicons-x-circle-fill-24:
|
:octicons-check-circle-fill-24:
| **NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT** | +|
:octicons-x-circle-fill-24:
|
:octicons-check-circle-fill-24:
|
:octicons-skip-fill-24:
| **NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE** | ## Parameters @@ -25,18 +33,20 @@ parameter is the name of the class/struct, and all remaining parameters name the : name of the type (class, struct) to serialize/deserialize `member` (in) -: name of the (public) member variable to serialize/deserialize; up to 64 members can be given as comma-separated list +: name of the (public) member variable to serialize/deserialize; up to 64 members can be given as a comma-separated list ## Default definition The macros add two functions to the namespace which take care of the serialization and deserialization: ```cpp -void to_json(nlohmann::json&, const type&); -void from_json(const nlohmann::json&, type&); // except (3) +template +void to_json(BasicJsonType&, const type&); +template +void from_json(const BasicJsonType&, type&); // except (3) ``` -See examples below for the concrete generated code. +See the examples below for the concrete generated code. ## Notes @@ -53,8 +63,6 @@ See examples below for the concrete generated code. - The current implementation is limited to at most 64 member variables. If you want to serialize/deserialize types with more than 64 member variables, you need to define the `to_json`/`from_json` functions manually. - - The macros only work for the [`nlohmann::json`](../json.md) type; other specializations such as - [`nlohmann::ordered_json`](../ordered_json.md) are currently unsupported. ## Examples @@ -82,7 +90,7 @@ See examples below for the concrete generated code. The macro is equivalent to: - ```cpp hl_lines="16 17 18 19 20 21 22 23 24 25 26 27 28" + ```cpp hl_lines="16 17 18 19 20 21 22 23 24 25 26 27 28 29 30" --8<-- "examples/nlohmann_define_type_non_intrusive_explicit.cpp" ``` @@ -90,7 +98,7 @@ See examples below for the concrete generated code. Consider the following complete example: - ```cpp hl_lines="22" + ```cpp hl_lines="21" --8<-- "examples/nlohmann_define_type_non_intrusive_with_default_macro.cpp" ``` @@ -111,7 +119,7 @@ See examples below for the concrete generated code. The macro is equivalent to: - ```cpp hl_lines="22 23 24 25 26 27 28 29 30 31 32 33 34 35" + ```cpp hl_lines="21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36" --8<-- "examples/nlohmann_define_type_non_intrusive_with_default_explicit.cpp" ``` @@ -140,18 +148,24 @@ See examples below for the concrete generated code. The macro is equivalent to: - ```cpp hl_lines="16 17 18 19 20 21" + ```cpp hl_lines="16 17 18 19 20 21 22" --8<-- "examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.cpp" ``` ## See also -- [NLOHMANN_DEFINE_TYPE_INTRUSIVE{_WITH_DEFAULT, _ONLY_SERIALIZE}](nlohmann_define_type_intrusive.md) +- [NLOHMANN_DEFINE_TYPE_INTRUSIVE, NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT, + NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE](nlohmann_define_type_intrusive.md) for a similar macro that can be defined _inside_ the type. +- [NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE, NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT, + NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE, + NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT, + NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE](nlohmann_define_derived_type.md) for similar macros for + derived types - [Arbitrary Type Conversions](../../features/arbitrary_types.md) for an overview. ## Version history 1. Added in version 3.9.0. 2. Added in version 3.11.0. -3. Added in version TODO. +3. Added in version 3.11.3. diff --git a/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum.md b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum.md index dc2cc8ecb8..fba6b217ab 100644 --- a/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum.md +++ b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum.md @@ -4,8 +4,8 @@ #define NLOHMANN_JSON_SERIALIZE_ENUM(type, conversion...) ``` -By default, enum values are serialized to JSON as integers. In some cases this could result in undesired behavior. If an -enum is modified or re-ordered after data has been serialized to JSON, the later de-serialized JSON data may be +By default, enum values are serialized to JSON as integers. In some cases, this could result in undesired behavior. If +an enum is modified or re-ordered after data has been serialized to JSON, the later deserialized JSON data may be undefined or a different enum value than was originally intended. The `NLOHMANN_JSON_SERIALIZE_ENUM` allows to define a user-defined serialization for every enumerator. @@ -20,7 +20,7 @@ The `NLOHMANN_JSON_SERIALIZE_ENUM` allows to define a user-defined serialization ## Default definition -The macros add two friend functions to the class which take care of the serialization and deserialization: +The macro adds two functions to the namespace which take care of the serialization and deserialization: ```cpp template @@ -37,7 +37,7 @@ inline void from_json(const BasicJsonType& j, type& e); !!! important "Important notes" - - When using [`template get()`](../basic_json/get.md), undefined JSON values will default to the first specified + - When using [`get()`](../basic_json/get.md), undefined JSON values will default to the first specified conversion. Select this default pair carefully. See example 1 below. - If an enum or JSON value is specified in multiple conversions, the first matching conversion from the top of the list will be returned when converting to or from JSON. See example 2 below. diff --git a/docs/mkdocs/docs/api/operator_gtgt.md b/docs/mkdocs/docs/api/operator_gtgt.md index e76cc0db7f..f332a7517c 100644 --- a/docs/mkdocs/docs/api/operator_gtgt.md +++ b/docs/mkdocs/docs/api/operator_gtgt.md @@ -21,8 +21,8 @@ the stream `i` ## Exceptions - Throws [`parse_error.101`](../home/exceptions.md#jsonexceptionparse_error101) in case of an unexpected token. -- Throws [`parse_error.102`](../home/exceptions.md#jsonexceptionparse_error102) if to_unicode fails or surrogate error. -- Throws [`parse_error.103`](../home/exceptions.md#jsonexceptionparse_error103) if to_unicode fails. +- Throws [`parse_error.102`](../home/exceptions.md#jsonexceptionparse_error102) if `to_unicode` fails or surrogate error. +- Throws [`parse_error.103`](../home/exceptions.md#jsonexceptionparse_error103) if `to_unicode` fails. ## Complexity diff --git a/docs/mkdocs/docs/api/operator_literal_json.md b/docs/mkdocs/docs/api/operator_literal_json.md index bc2b2cfc5d..fdf6e83695 100644 --- a/docs/mkdocs/docs/api/operator_literal_json.md +++ b/docs/mkdocs/docs/api/operator_literal_json.md @@ -2,6 +2,7 @@ ```cpp json operator ""_json(const char* s, std::size_t n); +json operator ""_json(const char8_t* s, std::size_t n); // since C++20 ``` This operator implements a user-defined string literal for JSON objects. It can be used by adding `#!cpp _json` to a @@ -17,7 +18,7 @@ using namespace nlohmann; ``` This is suggested to ease migration to the next major version release of the library. See -['JSON_USE_GLOBAL_UDLS`](macros/json_use_global_udls.md#notes) for details. +[`JSON_USE_GLOBAL_UDLS`](macros/json_use_global_udls.md#notes) for details. ## Parameters @@ -59,3 +60,4 @@ Linear. - Added in version 1.0.0. - Moved to namespace `nlohmann::literals::json_literals` in 3.11.0. +- Added `char8_t*` overload in 3.12.1. diff --git a/docs/mkdocs/docs/api/operator_literal_json_pointer.md b/docs/mkdocs/docs/api/operator_literal_json_pointer.md index 0e12440e19..2c3d6a13a8 100644 --- a/docs/mkdocs/docs/api/operator_literal_json_pointer.md +++ b/docs/mkdocs/docs/api/operator_literal_json_pointer.md @@ -2,6 +2,7 @@ ```cpp json_pointer operator ""_json_pointer(const char* s, std::size_t n); +json_pointer operator ""_json_pointer(const char8_t* s, std::size_t n); // since C++20 ``` This operator implements a user-defined string literal for JSON Pointers. It can be used by adding `#!cpp _json_pointer` @@ -16,7 +17,7 @@ using namespace nlohmann::literals::json_literals; using namespace nlohmann; ``` This is suggested to ease migration to the next major version release of the library. See -['JSON_USE_GLOBAL_UDLS`](macros/json_use_global_udls.md#notes) for details. +[`JSON_USE_GLOBAL_UDLS`](macros/json_use_global_udls.md#notes) for details. ## Parameters @@ -62,3 +63,4 @@ Linear. - Added in version 2.0.0. - Moved to namespace `nlohmann::literals::json_literals` in 3.11.0. +- Added `char8_t*` overload in 3.12.1. diff --git a/docs/mkdocs/docs/api/operator_ltlt.md b/docs/mkdocs/docs/api/operator_ltlt.md index 1718b3c9e8..8dedb05096 100644 --- a/docs/mkdocs/docs/api/operator_ltlt.md +++ b/docs/mkdocs/docs/api/operator_ltlt.md @@ -84,4 +84,4 @@ Linear. 1. Added in version 1.0.0. Added support for indentation character and deprecated `#!cpp std::ostream& operator>>(const basic_json& j, std::ostream& o)` in version 3.0.0. -3. Added in version 3.11.0. +2. Added in version 3.11.0. diff --git a/docs/mkdocs/docs/api/ordered_json.md b/docs/mkdocs/docs/api/ordered_json.md index 7cfd9f4dde..b28fe36f67 100644 --- a/docs/mkdocs/docs/api/ordered_json.md +++ b/docs/mkdocs/docs/api/ordered_json.md @@ -6,6 +6,13 @@ using ordered_json = basic_json; This type preserves the insertion order of object keys. +## Iterator invalidation + +The type is based on [`ordered_map`](ordered_map.md) which in turn uses a `std::vector` to store object elements. +Therefore, adding object elements can yield a reallocation in which case all iterators (including the +[`end()`](basic_json/end.md) iterator) and all references to the elements are invalidated. Also, any iterator or +reference after the insertion point will point to the same index, which is now a different value. + ## Examples ??? example diff --git a/docs/mkdocs/docs/api/ordered_map.md b/docs/mkdocs/docs/api/ordered_map.md index 160b85c28b..ca4934161c 100644 --- a/docs/mkdocs/docs/api/ordered_map.md +++ b/docs/mkdocs/docs/api/ordered_map.md @@ -23,6 +23,11 @@ A minimal map-like container that preserves insertion order for use within [`nlo `Allocator` : allocator type +## Iterator invalidation + +The type uses a `std::vector` to store object elements. Therefore, adding elements can yield a reallocation in which +case all iterators (including the `end()` iterator) and all references to the elements are invalidated. + ## Member types - **key_type** - key type (`Key`) diff --git a/docs/mkdocs/docs/community/code_of_conduct.md b/docs/mkdocs/docs/community/code_of_conduct.md new file mode 100644 index 0000000000..0685e0e578 --- /dev/null +++ b/docs/mkdocs/docs/community/code_of_conduct.md @@ -0,0 +1 @@ +--8<-- "../../../.github/CODE_OF_CONDUCT.md" diff --git a/docs/mkdocs/docs/community/contribution_guidelines.md b/docs/mkdocs/docs/community/contribution_guidelines.md new file mode 100644 index 0000000000..d439f1cfb6 --- /dev/null +++ b/docs/mkdocs/docs/community/contribution_guidelines.md @@ -0,0 +1 @@ +--8<-- "../../../.github/CONTRIBUTING.md" diff --git a/docs/mkdocs/docs/community/governance.md b/docs/mkdocs/docs/community/governance.md new file mode 100644 index 0000000000..7da85f3fe0 --- /dev/null +++ b/docs/mkdocs/docs/community/governance.md @@ -0,0 +1,122 @@ +# Governance + +The governance model for the JSON for Modern C++ project is a **Benevolent Dictator for Life (BDFL)** structure. As the +sole maintainer, [Niels Lohmann](https://github.com/nlohmann) is responsible for all key aspects of the project. The +project governance may evolve as the project grows, but any changes will be documented here and communicated to +contributors. + +## Overview + +This project is led by a benevolent dictator, [Niels Lohmann](https://github.com/nlohmann), and managed by the +community. That is, the community actively contributes to the day-to-day maintenance of the project, but the general +strategic line is drawn by the benevolent dictator. In case of disagreement, they have the last word. It is the +benevolent dictator’s job to resolve disputes within the community and to ensure that the project is able to progress in +a coordinated way. In turn, it is the community’s job to guide the decisions of the benevolent dictator through active +engagement and contribution. + +## Roles and responsibilities + +### Benevolent dictator (project lead) + +Typically, the benevolent dictator, or project lead, is self-appointed. However, because the community always has the +ability to fork, this person is fully answerable to the community. The project lead’s role is a difficult one: they set +the strategic objectives of the project and communicate these clearly to the community. They also have to understand the +community as a whole and strive to satisfy as many conflicting needs as possible, while ensuring that the project +survives in the long term. + +In many ways, the role of the benevolent dictator is less about dictatorship and more about diplomacy. The key is to +ensure that, as the project expands, the right people are given influence over it and the community rallies behind the +vision of the project lead. The lead’s job is then to ensure that the committers (see below) make the right decisions on +behalf of the project. Generally speaking, as long as the committers are aligned with the project’s strategy, the +project lead will allow them to proceed as they desire. + +### Committers + +Committers are contributors who have made several valuable contributions to the project and are now relied upon to both +write code directly to the repository and screen the contributions of others. In many cases they are programmers but it +is also possible that they contribute in a different role. Typically, a committer will focus on a specific aspect of the +project, and will bring a level of expertise and understanding that earns them the respect of the community and the +project lead. The role of committer is not an official one, it is simply a position that influential members of the +community will find themselves in as the project lead looks to them for guidance and support. + +Committers have no authority over the overall direction of the project. However, they do have the ear of the project +lead. It is a committer’s job to ensure that the lead is aware of the community’s needs and collective objectives, and +to help develop or elicit appropriate contributions to the project. Often, committers are given informal control over +their specific areas of responsibility, and are assigned rights to directly modify certain areas of the source code. +That is, although committers do not have explicit decision-making authority, they will often find that their actions are +synonymous with the decisions made by the lead. + +### Contributors + +Contributors are community members who either have no desire to become committers, or have not yet been given the +opportunity by the benevolent dictator. They make valuable contributions, such as those outlined in the list below, but +generally do not have the authority to make direct changes to the project code. Contributors engage with the project +through communication tools, such as email lists, and via reports and patches attached to issues in the issue tracker, +as detailed in our community tools document. + +Anyone can become a contributor. There is no expectation of commitment to the project, no specific skill requirements +and no selection process. To become a contributor, a community member simply has to perform one or more actions that are +beneficial to the project. + +Some contributors will already be engaging with the project as users, but will also find themselves doing one or more of +the following: + +- supporting new users (current users often provide the most effective new user support) +- reporting bugs +- identifying requirements +- supplying graphics and web design +- programming +- assisting with project infrastructure +- writing documentation +- fixing bugs +- adding features + +As contributors gain experience and familiarity with the project, they may find that the project lead starts relying on +them more and more. When this begins to happen, they gradually adopt the role of committer, as described above. + +### Users + +Users are community members who have a need for the project. They are the most important members of the community: +without them, the project would have no purpose. Anyone can be a user; there are no specific requirements. + +Users should be encouraged to participate in the life of the project and the community as much as possible. User +contributions enable the project team to ensure that they are satisfying the needs of those users. Common user +activities include (but are not limited to): + +- evangelising about the project +- informing developers of project strengths and weaknesses from a new user’s perspective +- providing moral support (a ‘thank you’ goes a long way) +- providing financial support + +Users who continue to engage with the project and its community will often find themselves becoming more and more +involved. Such users may then go on to become contributors, as described above. + +## Support + +All participants in the community are encouraged to provide support for new users within the project management +infrastructure. This support is provided as a way of growing the community. Those seeking support should recognise that +all support activity within the project is voluntary and is therefore provided as and when time allows. A user requiring +guaranteed response times or results should therefore seek to purchase a support contract from a vendor. (Of course, +that vendor should be an active member of the community.) However, for those willing to engage with the project on its +own terms, and willing to help support other users, the community support channels are ideal. + +## Contribution Process + +Anyone can contribute to the project, regardless of their skills, as there are many ways to contribute. For instance, a +contributor might be active on the project mailing list and issue tracker, or might supply patches. The various ways of +contributing are described in more detail in our roles in open source document. + +The developer mailing list is the most appropriate place for a contributor to ask for help when making their first +contribution. + +## Decision-Making Process + +The benevolent dictatorship model does not need a formal conflict resolution process, since the project lead’s word is +final. If the community chooses to question the wisdom of the actions of a committer, the project lead can review their +decisions by checking the email archives, and either uphold or reverse them. + +--- + +!!! quote "Source" + + The text was taken from http://oss-watch.ac.uk/resources/benevolentdictatorgovernancemodel. diff --git a/docs/mkdocs/docs/community/index.md b/docs/mkdocs/docs/community/index.md new file mode 100644 index 0000000000..caef17be36 --- /dev/null +++ b/docs/mkdocs/docs/community/index.md @@ -0,0 +1,7 @@ +# Community + +- [Code of Conduct](code_of_conduct.md) - the rules and norms of this project +- [Contribution Guidelines](contribution_guidelines.md) - guidelines how to contribute to this project +- [Governance](governance.md) - the governance model of this project +- [Quality Assurance](quality_assurance.md) - how the quality of this project is assured +- [Security Policy](security_policy.md) - the security policy of the project diff --git a/docs/mkdocs/docs/community/quality_assurance.md b/docs/mkdocs/docs/community/quality_assurance.md new file mode 100644 index 0000000000..7ddd93871e --- /dev/null +++ b/docs/mkdocs/docs/community/quality_assurance.md @@ -0,0 +1,219 @@ +# Quality assurance + +Ensuring quality is paramount for this project, particularly because [numerous other projects](../home/customers.md) +depend on it. Each commit to the library undergoes rigorous checks against the following requirements, and any +violations will result in a failed build. + +## C++ language compliance and compiler compatibility + +!!! success "Requirement: Compiler support" + + Any compiler with complete C++11 support can compile the library without warnings. + +- [x] The library is compiled with 50+ different C++ compilers with different operating systems and platforms, + including the oldest versions known to compile the library. + + ??? abstract "Compilers used in continuous integration" + + | Compiler | Architecture | Operating System | CI | + |----------------------------------------------|--------------|-----------------------------------|-----------| + | AppleClang 15.0.0.15000040; Xcode 15.0.1 | x86_64 | macOS 13.7.2 (Ventura) | GitHub | + | AppleClang 15.0.0.15000100; Xcode 15.1 | x86_64 | macOS 13.7.2 (Ventura) | GitHub | + | AppleClang 15.0.0.15000100; Xcode 15.2 | x86_64 | macOS 13.7.2 (Ventura) | GitHub | + | AppleClang 15.0.0.15000309; Xcode 15.3 | arm64 | macOS 14.7.2 (Sonoma) | GitHub | + | AppleClang 15.0.0.15000309; Xcode 15.4 | arm64 | macOS 14.7.2 (Sonoma) | GitHub | + | AppleClang 16.0.0.16000026; Xcode 16 | arm64 | macOS 15.2 (Sequoia) | GitHub | + | AppleClang 16.0.0.16000026; Xcode 16.1 | arm64 | macOS 15.2 (Sequoia) | GitHub | + | AppleClang 16.0.0.16000026; Xcode 16.2 | arm64 | macOS 15.2 (Sequoia) | GitHub | + | AppleClang 17.0.0.17000013; Xcode 16.3 | arm64 | macOS 15.5 (Sequoia) | GitHub | + | AppleClang 17.0.0.17000013; Xcode 16.4 | arm64 | macOS 15.5 (Sequoia) | GitHub | + | AppleClang 17.0.0.17000319; Xcode 26.0.1 | arm64 | macOS 15.5 (Sequoia) | GitHub | + | Clang 3.5.2 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Clang 3.6.2 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Clang 3.7.1 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Clang 3.8.1 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Clang 3.9.1 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Clang 4.0.1 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Clang 5.0.2 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Clang 6.0.1 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Clang 7.1.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Clang 8.0.1 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Clang 9.0.1 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Clang 10.0.1 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Clang 11.0.1 with GNU-like command-line | x86_64 | Windows Server 2022 (Build 20348) | GitHub | + | Clang 11.1.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Clang 12.0.1 with GNU-like command-line | x86_64 | Windows Server 2022 (Build 20348) | GitHub | + | Clang 12.0.1 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Clang 13.0.1 with GNU-like command-line | x86_64 | Windows Server 2022 (Build 20348) | GitHub | + | Clang 13.0.1 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Clang 14.0.6 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Clang 14.0.6 with GNU-like command-line | x86_64 | Windows Server 2022 (Build 20348) | GitHub | + | Clang 15.0.7 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Clang 15.0.7 with GNU-like command-line | x86_64 | Windows Server 2022 (Build 20348) | GitHub | + | Clang 16.0.6 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Clang 16.0.6 with GNU-like command-line | x86_64 | Windows Server 2022 (Build 20348) | GitHub | + | Clang 17.0.6 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Clang 18.1.8 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Clang 18.1.8 with GNU-like command-line | x86_64 | Windows Server 2022 (Build 20348) | GitHub | + | Clang 19.1.5 with MSVC-like command-line | x86_64 | Windows Server 2022 (Build 20348) | GitHub | + | Clang 19.1.7 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Clang 19.1.7 with GNU-like command-line | x86_64 | Windows Server 2022 (Build 20348) | GitHub | + | Clang 20.1.1 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Clang 20.1.8 with GNU-like command-line | x86_64 | Windows Server 2022 (Build 20348) | GitHub | + | Clang 21.0.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | Emscripten 4.0.6 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | GNU 4.8.5 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | GNU 4.9.3 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | GNU 5.5.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | GNU 6.4.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | GNU 7.5.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | GNU 8.5.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | GNU 9.3.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | GNU 9.4.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | GNU 9.5.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | GNU 10.5.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | GNU 11.4.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | GNU 11.5.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | GNU 12.2.0 (MinGW-W64 i686-ucrt-posix-dwarf) | x86_64 | Windows Server 2022 (Build 20348) | GitHub | + | GNU 12.2.0 (MinGW-W64 x86_64-ucrt-posix-seh) | x86_64 | Windows Server 2022 (Build 20348) | GitHub | + | GNU 12.4.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | GNU 13.3.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | GNU 14.2.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | GNU 14.2.0 | arm64 | Linux 6.1.100 | Cirrus CI | + | GNU 15.1.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub | + | icpc (ICC) 2021.5.0 20211109 | x86_64 | Ubuntu 20.04.3 LTS | GitHub | + | MSVC 19.0.24241.7 | x86 | Windows 8.1 | AppVeyor | + | MSVC 19.16.27035.0 | x86 | Windows-10 (Build 14393) | AppVeyor | + | MSVC 19.29.30157.0 | x86 | Windows-10 (Build 17763) | AppVeyor | + | MSVC 19.44.35214.0 | x86 | Windows Server 2022 (Build 20348) | GitHub | + | MSVC 19.44.35214.0 | x86_64 | Windows Server 2022 (Build 20348) | GitHub | + +- [x] The library is compiled with all C++ language revisions (C++11, C++14, C++17, C++20, C++23, and C++26) to detect + and fix language deprecations early. +- [x] The library is checked for compiler warnings: + - On Clang, `-Weverything` is used with 8 exceptions. + + ??? abstract "Clang warnings" + + ```cmake + --8<-- "../../../cmake/clang_flags.cmake" + ``` + + - On GCC, 300+ warnings are enabled with 8 exceptions. + + ??? abstract "GCC warnings" + + ```cmake + --8<-- "../../../cmake/gcc_flags.cmake" + ``` + +## C++ standard library compliance + +!!! success "Requirement: No prerequisites" + + The library has no prerequisites other than the Standard Template Library (STL). + +- [x] The library is compiled and tested with both [libc++](https://libcxx.llvm.org) and + [libstdc++](https://gcc.gnu.org/onlinedocs/libstdc++/) to detect subtle differences or incompatibilities. +- [x] The code checked with [Include What You Use (IWYU)](https://include-what-you-use.org) that all required standard + headers are included. +- [x] On Windows, the library is compiled with `` being included to detect and avoid common bugs. +- [x] The library is compiled with exceptions disabled to support alternative means of error handling. + +## Stable public API + +!!! success "Requirement: Stable public API" + + Any change to the library does not break the public API. + +- [x] All public API functions are tested with a variety of arguments. +- [x] The library is compiled and tested with different template arguments for number, string, array, and object types. +- [x] Unit tests cover all lines of the code base. +- [x] Every exception of the library is thrown in the test suite, and the error messages and exception ids are checked. + +!!! success "Requirement: Complete documentation" + + The public API is extensively documented. + +- [x] Every public API function has a dedicated page in the + [API reference documentation](https://json.nlohmann.me/api/basic_json/) with a self-contained code example. +- [x] All examples in the documentation are tested, and changes in their output are treated as an error. + +## Robust input processing + +!!! success "Requirement: Standards compliance" + + The library is compliant to JSON as defined in [RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259). + +- [x] The lexer is tested with all valid Unicode code points and all prefixes of all invalid Unicode code points. +- [x] The parser is tested against extensive correctness suites for JSON compliance. +- [x] In addition, the library is continuously fuzz-tested at [OSS-Fuzz](https://google.github.io/oss-fuzz/) where the + library is checked against billions of inputs. + +## Static analysis + +!!! success "Requirement: State-of-the-art code analysis" + + The code is checked with state-of-the-art static code analysis tools. + +- [x] The code is checked with the latest [Clang-Tidy](https://clang.llvm.org/extra/clang-tidy/). + + ??? abstract "Clang-Tidy configuration (.clang-tidy)" + + ```ini + --8<-- "../../../.clang-tidy" + ``` + +- [x] The code is checked with the latest [Cppcheck](https://cppcheck.sourceforge.io) with all warnings enabled. +- [x] The code is checked with the latest [Clang Static Analyzer](https://clang-analyzer.llvm.org) with 89 enabled + rules. +- [x] The code is checked with [Infer](https://fbinfer.com). +- [x] The code is checked with [Codacy](https://app.codacy.com/gh/nlohmann/json/dashboard). + +## Dynamic analysis + +!!! success "Requirement: Correctness" + + The library is checked for memory correctness and absence of undefined behavior. + +- [x] The test suite is executed with enabled [runtime assertions](https://json.nlohmann.me/features/assertions/) to + check invariants and preconditions of functions to detect undefined behavior. +- [x] The test suite is executed with [Valgrind](https://valgrind.org) (Memcheck) to detect memory leaks. +- [x] The test suite is executed with [Sanitizers](https://github.com/google/sanitizers) (address sanitizer, undefined + behavior sanitizer, integer overflow detection, nullability violations). + +## Style check + +!!! success "Requirement: Common code style" + + A common code style is used throughout all code files of the library. + +- [x] The code is formatted with [Artistic Style](https://astyle.sourceforge.net) (astyle) against a style configuration + that is also enforced in the CI. + + ??? abstract "Astyle configuration (tools/astyle/.astylerc)" + + ```ini + --8<-- "../../../tools/astyle/.astylerc" + ``` + +- [x] The code style is checked with [cpplint](https://github.com/cpplint/cpplint) with 61 enabled rules. + +## Simple integration + +!!! success "Requirement: Single header" + + The library can be used by adding a single header to a C++ project. + +- [x] An amalgamation script is used to check if the source code is exposed as a self-contained single-header file. +- [x] The test suite is checked against the amalgamated source file as well as the individual source file. + +!!! success "Requirement: CMake as primary development tool" + + All library functions are exposed and usable by CMake. + +- [x] All library options are exposed as [CMake options](https://json.nlohmann.me/integration/cmake/) and tested. +- [x] The library is tested against relevant CMake versions: + - CMake 3.5 (the earliest supported) + - CMake 3.31.6 (the latest 3.x release) + - CMake 4.0.0 (a very recent release) diff --git a/docs/mkdocs/docs/community/security_policy.md b/docs/mkdocs/docs/community/security_policy.md new file mode 100644 index 0000000000..fdee281cbc --- /dev/null +++ b/docs/mkdocs/docs/community/security_policy.md @@ -0,0 +1 @@ +--8<-- "../../../.github/SECURITY.md" diff --git a/docs/mkdocs/docs/css/custom.css b/docs/mkdocs/docs/css/custom.css index 7a1008b0bb..8940a782e1 100644 --- a/docs/mkdocs/docs/css/custom.css +++ b/docs/mkdocs/docs/css/custom.css @@ -1,4 +1,4 @@ -/* disable ligatures in code and preformatted blocks */ -code, pre { - font-variant-ligatures: none; +/* enable ligatures in code and preformatted blocks */ +.md-typeset code, .md-typeset pre { + font-variant-ligatures: common-ligatures; } diff --git a/docs/examples/README.cpp b/docs/mkdocs/docs/examples/README.cpp similarity index 100% rename from docs/examples/README.cpp rename to docs/mkdocs/docs/examples/README.cpp diff --git a/docs/examples/README.output b/docs/mkdocs/docs/examples/README.output similarity index 100% rename from docs/examples/README.output rename to docs/mkdocs/docs/examples/README.output diff --git a/docs/examples/accept__string.cpp b/docs/mkdocs/docs/examples/accept__string.cpp similarity index 100% rename from docs/examples/accept__string.cpp rename to docs/mkdocs/docs/examples/accept__string.cpp diff --git a/docs/examples/accept__string.output b/docs/mkdocs/docs/examples/accept__string.output similarity index 100% rename from docs/examples/accept__string.output rename to docs/mkdocs/docs/examples/accept__string.output diff --git a/docs/examples/array.cpp b/docs/mkdocs/docs/examples/array.cpp similarity index 100% rename from docs/examples/array.cpp rename to docs/mkdocs/docs/examples/array.cpp diff --git a/docs/examples/array.output b/docs/mkdocs/docs/examples/array.output similarity index 100% rename from docs/examples/array.output rename to docs/mkdocs/docs/examples/array.output diff --git a/docs/examples/array_t.cpp b/docs/mkdocs/docs/examples/array_t.cpp similarity index 100% rename from docs/examples/array_t.cpp rename to docs/mkdocs/docs/examples/array_t.cpp diff --git a/docs/examples/array_t.output b/docs/mkdocs/docs/examples/array_t.output similarity index 100% rename from docs/examples/array_t.output rename to docs/mkdocs/docs/examples/array_t.output diff --git a/docs/examples/at__json_pointer.cpp b/docs/mkdocs/docs/examples/at__json_pointer.cpp similarity index 100% rename from docs/examples/at__json_pointer.cpp rename to docs/mkdocs/docs/examples/at__json_pointer.cpp diff --git a/docs/examples/at__json_pointer.output b/docs/mkdocs/docs/examples/at__json_pointer.output similarity index 100% rename from docs/examples/at__json_pointer.output rename to docs/mkdocs/docs/examples/at__json_pointer.output diff --git a/docs/examples/at__json_pointer_const.cpp b/docs/mkdocs/docs/examples/at__json_pointer_const.cpp similarity index 100% rename from docs/examples/at__json_pointer_const.cpp rename to docs/mkdocs/docs/examples/at__json_pointer_const.cpp diff --git a/docs/examples/at__json_pointer_const.output b/docs/mkdocs/docs/examples/at__json_pointer_const.output similarity index 100% rename from docs/examples/at__json_pointer_const.output rename to docs/mkdocs/docs/examples/at__json_pointer_const.output diff --git a/docs/examples/at__keytype.c++17.cpp b/docs/mkdocs/docs/examples/at__keytype.c++17.cpp similarity index 100% rename from docs/examples/at__keytype.c++17.cpp rename to docs/mkdocs/docs/examples/at__keytype.c++17.cpp diff --git a/docs/examples/at__keytype.c++17.output b/docs/mkdocs/docs/examples/at__keytype.c++17.output similarity index 100% rename from docs/examples/at__keytype.c++17.output rename to docs/mkdocs/docs/examples/at__keytype.c++17.output diff --git a/docs/examples/at__keytype_const.c++17.cpp b/docs/mkdocs/docs/examples/at__keytype_const.c++17.cpp similarity index 100% rename from docs/examples/at__keytype_const.c++17.cpp rename to docs/mkdocs/docs/examples/at__keytype_const.c++17.cpp diff --git a/docs/examples/at__keytype_const.c++17.output b/docs/mkdocs/docs/examples/at__keytype_const.c++17.output similarity index 100% rename from docs/examples/at__keytype_const.c++17.output rename to docs/mkdocs/docs/examples/at__keytype_const.c++17.output diff --git a/docs/examples/at__object_t_key_type.cpp b/docs/mkdocs/docs/examples/at__object_t_key_type.cpp similarity index 100% rename from docs/examples/at__object_t_key_type.cpp rename to docs/mkdocs/docs/examples/at__object_t_key_type.cpp diff --git a/docs/examples/at__object_t_key_type.output b/docs/mkdocs/docs/examples/at__object_t_key_type.output similarity index 100% rename from docs/examples/at__object_t_key_type.output rename to docs/mkdocs/docs/examples/at__object_t_key_type.output diff --git a/docs/examples/at__object_t_key_type_const.cpp b/docs/mkdocs/docs/examples/at__object_t_key_type_const.cpp similarity index 100% rename from docs/examples/at__object_t_key_type_const.cpp rename to docs/mkdocs/docs/examples/at__object_t_key_type_const.cpp diff --git a/docs/examples/at__object_t_key_type_const.output b/docs/mkdocs/docs/examples/at__object_t_key_type_const.output similarity index 100% rename from docs/examples/at__object_t_key_type_const.output rename to docs/mkdocs/docs/examples/at__object_t_key_type_const.output diff --git a/docs/examples/at__size_type.cpp b/docs/mkdocs/docs/examples/at__size_type.cpp similarity index 100% rename from docs/examples/at__size_type.cpp rename to docs/mkdocs/docs/examples/at__size_type.cpp diff --git a/docs/examples/at__size_type.output b/docs/mkdocs/docs/examples/at__size_type.output similarity index 100% rename from docs/examples/at__size_type.output rename to docs/mkdocs/docs/examples/at__size_type.output diff --git a/docs/examples/at__size_type_const.cpp b/docs/mkdocs/docs/examples/at__size_type_const.cpp similarity index 100% rename from docs/examples/at__size_type_const.cpp rename to docs/mkdocs/docs/examples/at__size_type_const.cpp diff --git a/docs/examples/at__size_type_const.output b/docs/mkdocs/docs/examples/at__size_type_const.output similarity index 100% rename from docs/examples/at__size_type_const.output rename to docs/mkdocs/docs/examples/at__size_type_const.output diff --git a/docs/examples/back.cpp b/docs/mkdocs/docs/examples/back.cpp similarity index 100% rename from docs/examples/back.cpp rename to docs/mkdocs/docs/examples/back.cpp diff --git a/docs/examples/back.output b/docs/mkdocs/docs/examples/back.output similarity index 100% rename from docs/examples/back.output rename to docs/mkdocs/docs/examples/back.output diff --git a/docs/examples/basic_json__CompatibleType.cpp b/docs/mkdocs/docs/examples/basic_json__CompatibleType.cpp similarity index 100% rename from docs/examples/basic_json__CompatibleType.cpp rename to docs/mkdocs/docs/examples/basic_json__CompatibleType.cpp diff --git a/docs/examples/basic_json__CompatibleType.output b/docs/mkdocs/docs/examples/basic_json__CompatibleType.output similarity index 100% rename from docs/examples/basic_json__CompatibleType.output rename to docs/mkdocs/docs/examples/basic_json__CompatibleType.output diff --git a/docs/examples/basic_json__InputIt_InputIt.cpp b/docs/mkdocs/docs/examples/basic_json__InputIt_InputIt.cpp similarity index 100% rename from docs/examples/basic_json__InputIt_InputIt.cpp rename to docs/mkdocs/docs/examples/basic_json__InputIt_InputIt.cpp diff --git a/docs/examples/basic_json__InputIt_InputIt.output b/docs/mkdocs/docs/examples/basic_json__InputIt_InputIt.output similarity index 100% rename from docs/examples/basic_json__InputIt_InputIt.output rename to docs/mkdocs/docs/examples/basic_json__InputIt_InputIt.output diff --git a/docs/examples/basic_json__basic_json.cpp b/docs/mkdocs/docs/examples/basic_json__basic_json.cpp similarity index 100% rename from docs/examples/basic_json__basic_json.cpp rename to docs/mkdocs/docs/examples/basic_json__basic_json.cpp diff --git a/docs/examples/basic_json__basic_json.output b/docs/mkdocs/docs/examples/basic_json__basic_json.output similarity index 100% rename from docs/examples/basic_json__basic_json.output rename to docs/mkdocs/docs/examples/basic_json__basic_json.output diff --git a/docs/examples/basic_json__copyassignment.cpp b/docs/mkdocs/docs/examples/basic_json__copyassignment.cpp similarity index 100% rename from docs/examples/basic_json__copyassignment.cpp rename to docs/mkdocs/docs/examples/basic_json__copyassignment.cpp diff --git a/docs/examples/basic_json__copyassignment.output b/docs/mkdocs/docs/examples/basic_json__copyassignment.output similarity index 100% rename from docs/examples/basic_json__copyassignment.output rename to docs/mkdocs/docs/examples/basic_json__copyassignment.output diff --git a/docs/examples/basic_json__list_init_t.cpp b/docs/mkdocs/docs/examples/basic_json__list_init_t.cpp similarity index 100% rename from docs/examples/basic_json__list_init_t.cpp rename to docs/mkdocs/docs/examples/basic_json__list_init_t.cpp diff --git a/docs/examples/basic_json__list_init_t.output b/docs/mkdocs/docs/examples/basic_json__list_init_t.output similarity index 100% rename from docs/examples/basic_json__list_init_t.output rename to docs/mkdocs/docs/examples/basic_json__list_init_t.output diff --git a/docs/examples/basic_json__moveconstructor.cpp b/docs/mkdocs/docs/examples/basic_json__moveconstructor.cpp similarity index 100% rename from docs/examples/basic_json__moveconstructor.cpp rename to docs/mkdocs/docs/examples/basic_json__moveconstructor.cpp diff --git a/docs/examples/basic_json__moveconstructor.output b/docs/mkdocs/docs/examples/basic_json__moveconstructor.output similarity index 100% rename from docs/examples/basic_json__moveconstructor.output rename to docs/mkdocs/docs/examples/basic_json__moveconstructor.output diff --git a/docs/examples/basic_json__nullptr_t.cpp b/docs/mkdocs/docs/examples/basic_json__nullptr_t.cpp similarity index 100% rename from docs/examples/basic_json__nullptr_t.cpp rename to docs/mkdocs/docs/examples/basic_json__nullptr_t.cpp diff --git a/docs/examples/basic_json__nullptr_t.output b/docs/mkdocs/docs/examples/basic_json__nullptr_t.output similarity index 100% rename from docs/examples/basic_json__nullptr_t.output rename to docs/mkdocs/docs/examples/basic_json__nullptr_t.output diff --git a/docs/examples/basic_json__size_type_basic_json.cpp b/docs/mkdocs/docs/examples/basic_json__size_type_basic_json.cpp similarity index 100% rename from docs/examples/basic_json__size_type_basic_json.cpp rename to docs/mkdocs/docs/examples/basic_json__size_type_basic_json.cpp diff --git a/docs/examples/basic_json__size_type_basic_json.output b/docs/mkdocs/docs/examples/basic_json__size_type_basic_json.output similarity index 100% rename from docs/examples/basic_json__size_type_basic_json.output rename to docs/mkdocs/docs/examples/basic_json__size_type_basic_json.output diff --git a/docs/examples/basic_json__value_t.cpp b/docs/mkdocs/docs/examples/basic_json__value_t.cpp similarity index 100% rename from docs/examples/basic_json__value_t.cpp rename to docs/mkdocs/docs/examples/basic_json__value_t.cpp diff --git a/docs/examples/basic_json__value_t.output b/docs/mkdocs/docs/examples/basic_json__value_t.output similarity index 100% rename from docs/examples/basic_json__value_t.output rename to docs/mkdocs/docs/examples/basic_json__value_t.output diff --git a/docs/examples/begin.cpp b/docs/mkdocs/docs/examples/begin.cpp similarity index 100% rename from docs/examples/begin.cpp rename to docs/mkdocs/docs/examples/begin.cpp diff --git a/docs/examples/begin.output b/docs/mkdocs/docs/examples/begin.output similarity index 100% rename from docs/examples/begin.output rename to docs/mkdocs/docs/examples/begin.output diff --git a/docs/examples/binary.cpp b/docs/mkdocs/docs/examples/binary.cpp similarity index 100% rename from docs/examples/binary.cpp rename to docs/mkdocs/docs/examples/binary.cpp diff --git a/docs/examples/binary.output b/docs/mkdocs/docs/examples/binary.output similarity index 100% rename from docs/examples/binary.output rename to docs/mkdocs/docs/examples/binary.output diff --git a/docs/examples/binary_t.cpp b/docs/mkdocs/docs/examples/binary_t.cpp similarity index 100% rename from docs/examples/binary_t.cpp rename to docs/mkdocs/docs/examples/binary_t.cpp diff --git a/docs/examples/binary_t.output b/docs/mkdocs/docs/examples/binary_t.output similarity index 100% rename from docs/examples/binary_t.output rename to docs/mkdocs/docs/examples/binary_t.output diff --git a/docs/examples/boolean_t.cpp b/docs/mkdocs/docs/examples/boolean_t.cpp similarity index 100% rename from docs/examples/boolean_t.cpp rename to docs/mkdocs/docs/examples/boolean_t.cpp diff --git a/docs/examples/boolean_t.output b/docs/mkdocs/docs/examples/boolean_t.output similarity index 100% rename from docs/examples/boolean_t.output rename to docs/mkdocs/docs/examples/boolean_t.output diff --git a/docs/examples/byte_container_with_subtype__byte_container_with_subtype.cpp b/docs/mkdocs/docs/examples/byte_container_with_subtype__byte_container_with_subtype.cpp similarity index 100% rename from docs/examples/byte_container_with_subtype__byte_container_with_subtype.cpp rename to docs/mkdocs/docs/examples/byte_container_with_subtype__byte_container_with_subtype.cpp diff --git a/docs/examples/byte_container_with_subtype__byte_container_with_subtype.output b/docs/mkdocs/docs/examples/byte_container_with_subtype__byte_container_with_subtype.output similarity index 100% rename from docs/examples/byte_container_with_subtype__byte_container_with_subtype.output rename to docs/mkdocs/docs/examples/byte_container_with_subtype__byte_container_with_subtype.output diff --git a/docs/examples/byte_container_with_subtype__clear_subtype.cpp b/docs/mkdocs/docs/examples/byte_container_with_subtype__clear_subtype.cpp similarity index 100% rename from docs/examples/byte_container_with_subtype__clear_subtype.cpp rename to docs/mkdocs/docs/examples/byte_container_with_subtype__clear_subtype.cpp diff --git a/docs/examples/byte_container_with_subtype__clear_subtype.output b/docs/mkdocs/docs/examples/byte_container_with_subtype__clear_subtype.output similarity index 100% rename from docs/examples/byte_container_with_subtype__clear_subtype.output rename to docs/mkdocs/docs/examples/byte_container_with_subtype__clear_subtype.output diff --git a/docs/examples/byte_container_with_subtype__has_subtype.cpp b/docs/mkdocs/docs/examples/byte_container_with_subtype__has_subtype.cpp similarity index 100% rename from docs/examples/byte_container_with_subtype__has_subtype.cpp rename to docs/mkdocs/docs/examples/byte_container_with_subtype__has_subtype.cpp diff --git a/docs/examples/byte_container_with_subtype__has_subtype.output b/docs/mkdocs/docs/examples/byte_container_with_subtype__has_subtype.output similarity index 100% rename from docs/examples/byte_container_with_subtype__has_subtype.output rename to docs/mkdocs/docs/examples/byte_container_with_subtype__has_subtype.output diff --git a/docs/examples/byte_container_with_subtype__set_subtype.cpp b/docs/mkdocs/docs/examples/byte_container_with_subtype__set_subtype.cpp similarity index 100% rename from docs/examples/byte_container_with_subtype__set_subtype.cpp rename to docs/mkdocs/docs/examples/byte_container_with_subtype__set_subtype.cpp diff --git a/docs/examples/byte_container_with_subtype__set_subtype.output b/docs/mkdocs/docs/examples/byte_container_with_subtype__set_subtype.output similarity index 100% rename from docs/examples/byte_container_with_subtype__set_subtype.output rename to docs/mkdocs/docs/examples/byte_container_with_subtype__set_subtype.output diff --git a/docs/examples/byte_container_with_subtype__subtype.cpp b/docs/mkdocs/docs/examples/byte_container_with_subtype__subtype.cpp similarity index 100% rename from docs/examples/byte_container_with_subtype__subtype.cpp rename to docs/mkdocs/docs/examples/byte_container_with_subtype__subtype.cpp diff --git a/docs/examples/byte_container_with_subtype__subtype.output b/docs/mkdocs/docs/examples/byte_container_with_subtype__subtype.output similarity index 100% rename from docs/examples/byte_container_with_subtype__subtype.output rename to docs/mkdocs/docs/examples/byte_container_with_subtype__subtype.output diff --git a/docs/examples/cbegin.cpp b/docs/mkdocs/docs/examples/cbegin.cpp similarity index 100% rename from docs/examples/cbegin.cpp rename to docs/mkdocs/docs/examples/cbegin.cpp diff --git a/docs/examples/cbegin.output b/docs/mkdocs/docs/examples/cbegin.output similarity index 100% rename from docs/examples/cbegin.output rename to docs/mkdocs/docs/examples/cbegin.output diff --git a/docs/examples/cbor_tag_handler_t.cpp b/docs/mkdocs/docs/examples/cbor_tag_handler_t.cpp similarity index 100% rename from docs/examples/cbor_tag_handler_t.cpp rename to docs/mkdocs/docs/examples/cbor_tag_handler_t.cpp diff --git a/docs/examples/cbor_tag_handler_t.output b/docs/mkdocs/docs/examples/cbor_tag_handler_t.output similarity index 100% rename from docs/examples/cbor_tag_handler_t.output rename to docs/mkdocs/docs/examples/cbor_tag_handler_t.output diff --git a/docs/examples/cend.cpp b/docs/mkdocs/docs/examples/cend.cpp similarity index 100% rename from docs/examples/cend.cpp rename to docs/mkdocs/docs/examples/cend.cpp diff --git a/docs/examples/cend.output b/docs/mkdocs/docs/examples/cend.output similarity index 100% rename from docs/examples/cend.output rename to docs/mkdocs/docs/examples/cend.output diff --git a/docs/examples/clear.cpp b/docs/mkdocs/docs/examples/clear.cpp similarity index 100% rename from docs/examples/clear.cpp rename to docs/mkdocs/docs/examples/clear.cpp diff --git a/docs/examples/clear.output b/docs/mkdocs/docs/examples/clear.output similarity index 100% rename from docs/examples/clear.output rename to docs/mkdocs/docs/examples/clear.output diff --git a/docs/mkdocs/docs/examples/comments.cpp b/docs/mkdocs/docs/examples/comments.cpp new file mode 100644 index 0000000000..fc8a0ac34a --- /dev/null +++ b/docs/mkdocs/docs/examples/comments.cpp @@ -0,0 +1,31 @@ + +#include +#include + +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'; +} diff --git a/docs/mkdocs/docs/examples/comments.output b/docs/mkdocs/docs/examples/comments.output new file mode 100644 index 0000000000..6e0d1c4622 --- /dev/null +++ b/docs/mkdocs/docs/examples/comments.output @@ -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: ' { /'; expected string literal +{ + "planets": [ + "Mercury", + "Venus", + "Earth", + "Mars", + "Jupiter", + "Uranus", + "Neptune" + ] +} diff --git a/docs/examples/contains__json_pointer.cpp b/docs/mkdocs/docs/examples/contains__json_pointer.cpp similarity index 100% rename from docs/examples/contains__json_pointer.cpp rename to docs/mkdocs/docs/examples/contains__json_pointer.cpp diff --git a/docs/examples/contains__json_pointer.output b/docs/mkdocs/docs/examples/contains__json_pointer.output similarity index 100% rename from docs/examples/contains__json_pointer.output rename to docs/mkdocs/docs/examples/contains__json_pointer.output diff --git a/docs/examples/contains__keytype.c++17.cpp b/docs/mkdocs/docs/examples/contains__keytype.c++17.cpp similarity index 100% rename from docs/examples/contains__keytype.c++17.cpp rename to docs/mkdocs/docs/examples/contains__keytype.c++17.cpp diff --git a/docs/examples/contains__keytype.c++17.output b/docs/mkdocs/docs/examples/contains__keytype.c++17.output similarity index 100% rename from docs/examples/contains__keytype.c++17.output rename to docs/mkdocs/docs/examples/contains__keytype.c++17.output diff --git a/docs/examples/contains__object_t_key_type.cpp b/docs/mkdocs/docs/examples/contains__object_t_key_type.cpp similarity index 100% rename from docs/examples/contains__object_t_key_type.cpp rename to docs/mkdocs/docs/examples/contains__object_t_key_type.cpp diff --git a/docs/examples/contains__object_t_key_type.output b/docs/mkdocs/docs/examples/contains__object_t_key_type.output similarity index 100% rename from docs/examples/contains__object_t_key_type.output rename to docs/mkdocs/docs/examples/contains__object_t_key_type.output diff --git a/docs/examples/count__keytype.c++17.cpp b/docs/mkdocs/docs/examples/count__keytype.c++17.cpp similarity index 100% rename from docs/examples/count__keytype.c++17.cpp rename to docs/mkdocs/docs/examples/count__keytype.c++17.cpp diff --git a/docs/examples/count__keytype.c++17.output b/docs/mkdocs/docs/examples/count__keytype.c++17.output similarity index 100% rename from docs/examples/count__keytype.c++17.output rename to docs/mkdocs/docs/examples/count__keytype.c++17.output diff --git a/docs/examples/count__object_t_key_type.cpp b/docs/mkdocs/docs/examples/count__object_t_key_type.cpp similarity index 100% rename from docs/examples/count__object_t_key_type.cpp rename to docs/mkdocs/docs/examples/count__object_t_key_type.cpp diff --git a/docs/examples/count__object_t_key_type.output b/docs/mkdocs/docs/examples/count__object_t_key_type.output similarity index 100% rename from docs/examples/count__object_t_key_type.output rename to docs/mkdocs/docs/examples/count__object_t_key_type.output diff --git a/docs/examples/crbegin.cpp b/docs/mkdocs/docs/examples/crbegin.cpp similarity index 100% rename from docs/examples/crbegin.cpp rename to docs/mkdocs/docs/examples/crbegin.cpp diff --git a/docs/examples/crbegin.output b/docs/mkdocs/docs/examples/crbegin.output similarity index 100% rename from docs/examples/crbegin.output rename to docs/mkdocs/docs/examples/crbegin.output diff --git a/docs/examples/crend.cpp b/docs/mkdocs/docs/examples/crend.cpp similarity index 100% rename from docs/examples/crend.cpp rename to docs/mkdocs/docs/examples/crend.cpp diff --git a/docs/examples/crend.output b/docs/mkdocs/docs/examples/crend.output similarity index 100% rename from docs/examples/crend.output rename to docs/mkdocs/docs/examples/crend.output diff --git a/docs/examples/default_object_comparator_t.cpp b/docs/mkdocs/docs/examples/default_object_comparator_t.cpp similarity index 100% rename from docs/examples/default_object_comparator_t.cpp rename to docs/mkdocs/docs/examples/default_object_comparator_t.cpp diff --git a/docs/examples/default_object_comparator_t.output b/docs/mkdocs/docs/examples/default_object_comparator_t.output similarity index 100% rename from docs/examples/default_object_comparator_t.output rename to docs/mkdocs/docs/examples/default_object_comparator_t.output diff --git a/docs/mkdocs/docs/examples/diagnostic_positions.cpp b/docs/mkdocs/docs/examples/diagnostic_positions.cpp new file mode 100644 index 0000000000..259344cd05 --- /dev/null +++ b/docs/mkdocs/docs/examples/diagnostic_positions.cpp @@ -0,0 +1,51 @@ +#include + +#define JSON_DIAGNOSTIC_POSITIONS 1 +#include + +using json = nlohmann::json; + +int main() +{ + std::string json_string = R"( + { + "address": { + "street": "Fake Street", + "housenumber": 1 + } + } + )"; + json j = json::parse(json_string); + + std::cout << "Root diagnostic positions: \n"; + std::cout << "\tstart_pos: " << j.start_pos() << '\n'; + std::cout << "\tend_pos:" << j.end_pos() << "\n"; + std::cout << "Original string: \n"; + std::cout << "{\n \"address\": {\n \"street\": \"Fake Street\",\n \"housenumber\": 1\n }\n }" << "\n"; + std::cout << "Parsed string: \n"; + std::cout << json_string.substr(j.start_pos(), j.end_pos() - j.start_pos()) << "\n\n"; + + std::cout << "address diagnostic positions: \n"; + std::cout << "\tstart_pos:" << j["address"].start_pos() << '\n'; + std::cout << "\tend_pos:" << j["address"].end_pos() << "\n\n"; + std::cout << "Original string: \n"; + std::cout << "{ \"street\": \"Fake Street\",\n \"housenumber\": 1\n }" << "\n"; + std::cout << "Parsed string: \n"; + std::cout << json_string.substr(j["address"].start_pos(), j["address"].end_pos() - j["address"].start_pos()) << "\n\n"; + + std::cout << "street diagnostic positions: \n"; + std::cout << "\tstart_pos:" << j["address"]["street"].start_pos() << '\n'; + std::cout << "\tend_pos:" << j["address"]["street"].end_pos() << "\n\n"; + std::cout << "Original string: \n"; + std::cout << "\"Fake Street\"" << "\n"; + std::cout << "Parsed string: \n"; + std::cout << json_string.substr(j["address"]["street"].start_pos(), j["address"]["street"].end_pos() - j["address"]["street"].start_pos()) << "\n\n"; + + std::cout << "housenumber diagnostic positions: \n"; + std::cout << "\tstart_pos:" << j["address"]["housenumber"].start_pos() << '\n'; + std::cout << "\tend_pos:" << j["address"]["housenumber"].end_pos() << "\n\n"; + std::cout << "Original string: \n"; + std::cout << "1" << "\n"; + std::cout << "Parsed string: \n"; + std::cout << json_string.substr(j["address"]["housenumber"].start_pos(), j["address"]["housenumber"].end_pos() - j["address"]["housenumber"].start_pos()) << "\n\n"; +} diff --git a/docs/mkdocs/docs/examples/diagnostic_positions.output b/docs/mkdocs/docs/examples/diagnostic_positions.output new file mode 100644 index 0000000000..a6becc0e6d --- /dev/null +++ b/docs/mkdocs/docs/examples/diagnostic_positions.output @@ -0,0 +1,50 @@ +Root diagnostic positions: + start_pos: 5 + end_pos:109 +Original string: +{ + "address": { + "street": "Fake Street", + "housenumber": 1 + } + } +Parsed string: +{ + "address": { + "street": "Fake Street", + "housenumber": 1 + } + } + +address diagnostic positions: + start_pos:26 + end_pos:103 + +Original string: +{ "street": "Fake Street", + "housenumber": 1 + } +Parsed string: +{ + "street": "Fake Street", + "housenumber": 1 + } + +street diagnostic positions: + start_pos:50 + end_pos:63 + +Original string: +"Fake Street" +Parsed string: +"Fake Street" + +housenumber diagnostic positions: + start_pos:92 + end_pos:93 + +Original string: +1 +Parsed string: +1 + diff --git a/docs/mkdocs/docs/examples/diagnostic_positions_exception.cpp b/docs/mkdocs/docs/examples/diagnostic_positions_exception.cpp new file mode 100644 index 0000000000..085a25c190 --- /dev/null +++ b/docs/mkdocs/docs/examples/diagnostic_positions_exception.cpp @@ -0,0 +1,30 @@ +#include + +#define JSON_DIAGNOSTIC_POSITIONS 1 +#include + +using json = nlohmann::json; + +/* Demonstration of type error exception with diagnostic positions support enabled */ +int main() +{ + //Invalid json string - housenumber type must be int instead of string + const std::string json_invalid_string = R"( + { + "address": { + "street": "Fake Street", + "housenumber": "1" + } + } + )"; + json j = json::parse(json_invalid_string); + try + { + int housenumber = j["address"]["housenumber"]; + std::cout << housenumber; + } + catch (const json::exception& e) + { + std::cout << e.what() << '\n'; + } +} diff --git a/docs/mkdocs/docs/examples/diagnostic_positions_exception.output b/docs/mkdocs/docs/examples/diagnostic_positions_exception.output new file mode 100644 index 0000000000..564deb34d3 --- /dev/null +++ b/docs/mkdocs/docs/examples/diagnostic_positions_exception.output @@ -0,0 +1 @@ +[json.exception.type_error.302] (bytes 92-95) type must be number, but is string diff --git a/docs/examples/diagnostics_extended.cpp b/docs/mkdocs/docs/examples/diagnostics_extended.cpp similarity index 100% rename from docs/examples/diagnostics_extended.cpp rename to docs/mkdocs/docs/examples/diagnostics_extended.cpp diff --git a/docs/examples/diagnostics_extended.output b/docs/mkdocs/docs/examples/diagnostics_extended.output similarity index 100% rename from docs/examples/diagnostics_extended.output rename to docs/mkdocs/docs/examples/diagnostics_extended.output diff --git a/docs/mkdocs/docs/examples/diagnostics_extended_positions.cpp b/docs/mkdocs/docs/examples/diagnostics_extended_positions.cpp new file mode 100644 index 0000000000..9a31ac3564 --- /dev/null +++ b/docs/mkdocs/docs/examples/diagnostics_extended_positions.cpp @@ -0,0 +1,31 @@ +#include + +#define JSON_DIAGNOSTICS 1 +#define JSON_DIAGNOSTIC_POSITIONS 1 +#include + +using json = nlohmann::json; + +/* Demonstration of type error exception with diagnostic positions support enabled */ +int main() +{ + //Invalid json string - housenumber type must be int instead of string + const std::string json_invalid_string = R"( + { + "address": { + "street": "Fake Street", + "housenumber": "1" + } + } + )"; + json j = json::parse(json_invalid_string); + try + { + int housenumber = j["address"]["housenumber"]; + std::cout << housenumber; + } + catch (const json::exception& e) + { + std::cout << e.what() << '\n'; + } +} diff --git a/docs/mkdocs/docs/examples/diagnostics_extended_positions.output b/docs/mkdocs/docs/examples/diagnostics_extended_positions.output new file mode 100644 index 0000000000..35096d9467 --- /dev/null +++ b/docs/mkdocs/docs/examples/diagnostics_extended_positions.output @@ -0,0 +1 @@ +[json.exception.type_error.302] (/address/housenumber) (bytes 92-95) type must be number, but is string diff --git a/docs/examples/diagnostics_standard.cpp b/docs/mkdocs/docs/examples/diagnostics_standard.cpp similarity index 100% rename from docs/examples/diagnostics_standard.cpp rename to docs/mkdocs/docs/examples/diagnostics_standard.cpp diff --git a/docs/examples/diagnostics_standard.output b/docs/mkdocs/docs/examples/diagnostics_standard.output similarity index 100% rename from docs/examples/diagnostics_standard.output rename to docs/mkdocs/docs/examples/diagnostics_standard.output diff --git a/docs/examples/diff.cpp b/docs/mkdocs/docs/examples/diff.cpp similarity index 100% rename from docs/examples/diff.cpp rename to docs/mkdocs/docs/examples/diff.cpp diff --git a/docs/examples/diff.output b/docs/mkdocs/docs/examples/diff.output similarity index 100% rename from docs/examples/diff.output rename to docs/mkdocs/docs/examples/diff.output diff --git a/docs/examples/dump.cpp b/docs/mkdocs/docs/examples/dump.cpp similarity index 100% rename from docs/examples/dump.cpp rename to docs/mkdocs/docs/examples/dump.cpp diff --git a/docs/examples/dump.output b/docs/mkdocs/docs/examples/dump.output similarity index 100% rename from docs/examples/dump.output rename to docs/mkdocs/docs/examples/dump.output diff --git a/docs/examples/emplace.cpp b/docs/mkdocs/docs/examples/emplace.cpp similarity index 100% rename from docs/examples/emplace.cpp rename to docs/mkdocs/docs/examples/emplace.cpp diff --git a/docs/examples/emplace.output b/docs/mkdocs/docs/examples/emplace.output similarity index 100% rename from docs/examples/emplace.output rename to docs/mkdocs/docs/examples/emplace.output diff --git a/docs/examples/emplace_back.cpp b/docs/mkdocs/docs/examples/emplace_back.cpp similarity index 100% rename from docs/examples/emplace_back.cpp rename to docs/mkdocs/docs/examples/emplace_back.cpp diff --git a/docs/examples/emplace_back.output b/docs/mkdocs/docs/examples/emplace_back.output similarity index 100% rename from docs/examples/emplace_back.output rename to docs/mkdocs/docs/examples/emplace_back.output diff --git a/docs/examples/empty.cpp b/docs/mkdocs/docs/examples/empty.cpp similarity index 100% rename from docs/examples/empty.cpp rename to docs/mkdocs/docs/examples/empty.cpp diff --git a/docs/examples/empty.output b/docs/mkdocs/docs/examples/empty.output similarity index 100% rename from docs/examples/empty.output rename to docs/mkdocs/docs/examples/empty.output diff --git a/docs/examples/end.cpp b/docs/mkdocs/docs/examples/end.cpp similarity index 100% rename from docs/examples/end.cpp rename to docs/mkdocs/docs/examples/end.cpp diff --git a/docs/examples/end.output b/docs/mkdocs/docs/examples/end.output similarity index 100% rename from docs/examples/end.output rename to docs/mkdocs/docs/examples/end.output diff --git a/docs/examples/erase__IteratorType.cpp b/docs/mkdocs/docs/examples/erase__IteratorType.cpp similarity index 100% rename from docs/examples/erase__IteratorType.cpp rename to docs/mkdocs/docs/examples/erase__IteratorType.cpp diff --git a/docs/examples/erase__IteratorType.output b/docs/mkdocs/docs/examples/erase__IteratorType.output similarity index 100% rename from docs/examples/erase__IteratorType.output rename to docs/mkdocs/docs/examples/erase__IteratorType.output diff --git a/docs/examples/erase__IteratorType_IteratorType.cpp b/docs/mkdocs/docs/examples/erase__IteratorType_IteratorType.cpp similarity index 100% rename from docs/examples/erase__IteratorType_IteratorType.cpp rename to docs/mkdocs/docs/examples/erase__IteratorType_IteratorType.cpp diff --git a/docs/examples/erase__IteratorType_IteratorType.output b/docs/mkdocs/docs/examples/erase__IteratorType_IteratorType.output similarity index 100% rename from docs/examples/erase__IteratorType_IteratorType.output rename to docs/mkdocs/docs/examples/erase__IteratorType_IteratorType.output diff --git a/docs/examples/erase__keytype.c++17.cpp b/docs/mkdocs/docs/examples/erase__keytype.c++17.cpp similarity index 100% rename from docs/examples/erase__keytype.c++17.cpp rename to docs/mkdocs/docs/examples/erase__keytype.c++17.cpp diff --git a/docs/examples/erase__keytype.c++17.output b/docs/mkdocs/docs/examples/erase__keytype.c++17.output similarity index 100% rename from docs/examples/erase__keytype.c++17.output rename to docs/mkdocs/docs/examples/erase__keytype.c++17.output diff --git a/docs/examples/erase__object_t_key_type.cpp b/docs/mkdocs/docs/examples/erase__object_t_key_type.cpp similarity index 100% rename from docs/examples/erase__object_t_key_type.cpp rename to docs/mkdocs/docs/examples/erase__object_t_key_type.cpp diff --git a/docs/examples/erase__object_t_key_type.output b/docs/mkdocs/docs/examples/erase__object_t_key_type.output similarity index 100% rename from docs/examples/erase__object_t_key_type.output rename to docs/mkdocs/docs/examples/erase__object_t_key_type.output diff --git a/docs/examples/erase__size_type.cpp b/docs/mkdocs/docs/examples/erase__size_type.cpp similarity index 100% rename from docs/examples/erase__size_type.cpp rename to docs/mkdocs/docs/examples/erase__size_type.cpp diff --git a/docs/examples/erase__size_type.output b/docs/mkdocs/docs/examples/erase__size_type.output similarity index 100% rename from docs/examples/erase__size_type.output rename to docs/mkdocs/docs/examples/erase__size_type.output diff --git a/docs/examples/error_handler_t.cpp b/docs/mkdocs/docs/examples/error_handler_t.cpp similarity index 100% rename from docs/examples/error_handler_t.cpp rename to docs/mkdocs/docs/examples/error_handler_t.cpp diff --git a/docs/examples/error_handler_t.output b/docs/mkdocs/docs/examples/error_handler_t.output similarity index 100% rename from docs/examples/error_handler_t.output rename to docs/mkdocs/docs/examples/error_handler_t.output diff --git a/docs/examples/exception.cpp b/docs/mkdocs/docs/examples/exception.cpp similarity index 100% rename from docs/examples/exception.cpp rename to docs/mkdocs/docs/examples/exception.cpp diff --git a/docs/examples/exception.output b/docs/mkdocs/docs/examples/exception.output similarity index 100% rename from docs/examples/exception.output rename to docs/mkdocs/docs/examples/exception.output diff --git a/docs/examples/find__keytype.c++17.cpp b/docs/mkdocs/docs/examples/find__keytype.c++17.cpp similarity index 100% rename from docs/examples/find__keytype.c++17.cpp rename to docs/mkdocs/docs/examples/find__keytype.c++17.cpp diff --git a/docs/examples/find__keytype.c++17.output b/docs/mkdocs/docs/examples/find__keytype.c++17.output similarity index 100% rename from docs/examples/find__keytype.c++17.output rename to docs/mkdocs/docs/examples/find__keytype.c++17.output diff --git a/docs/examples/find__object_t_key_type.cpp b/docs/mkdocs/docs/examples/find__object_t_key_type.cpp similarity index 100% rename from docs/examples/find__object_t_key_type.cpp rename to docs/mkdocs/docs/examples/find__object_t_key_type.cpp diff --git a/docs/examples/find__object_t_key_type.output b/docs/mkdocs/docs/examples/find__object_t_key_type.output similarity index 100% rename from docs/examples/find__object_t_key_type.output rename to docs/mkdocs/docs/examples/find__object_t_key_type.output diff --git a/docs/examples/flatten.cpp b/docs/mkdocs/docs/examples/flatten.cpp similarity index 100% rename from docs/examples/flatten.cpp rename to docs/mkdocs/docs/examples/flatten.cpp diff --git a/docs/examples/flatten.output b/docs/mkdocs/docs/examples/flatten.output similarity index 100% rename from docs/examples/flatten.output rename to docs/mkdocs/docs/examples/flatten.output diff --git a/docs/examples/from_bjdata.cpp b/docs/mkdocs/docs/examples/from_bjdata.cpp similarity index 100% rename from docs/examples/from_bjdata.cpp rename to docs/mkdocs/docs/examples/from_bjdata.cpp diff --git a/docs/examples/from_bjdata.output b/docs/mkdocs/docs/examples/from_bjdata.output similarity index 100% rename from docs/examples/from_bjdata.output rename to docs/mkdocs/docs/examples/from_bjdata.output diff --git a/docs/examples/from_bson.cpp b/docs/mkdocs/docs/examples/from_bson.cpp similarity index 100% rename from docs/examples/from_bson.cpp rename to docs/mkdocs/docs/examples/from_bson.cpp diff --git a/docs/examples/from_bson.output b/docs/mkdocs/docs/examples/from_bson.output similarity index 100% rename from docs/examples/from_bson.output rename to docs/mkdocs/docs/examples/from_bson.output diff --git a/docs/examples/from_cbor.cpp b/docs/mkdocs/docs/examples/from_cbor.cpp similarity index 100% rename from docs/examples/from_cbor.cpp rename to docs/mkdocs/docs/examples/from_cbor.cpp diff --git a/docs/examples/from_cbor.output b/docs/mkdocs/docs/examples/from_cbor.output similarity index 100% rename from docs/examples/from_cbor.output rename to docs/mkdocs/docs/examples/from_cbor.output diff --git a/docs/examples/from_json__default_constructible.cpp b/docs/mkdocs/docs/examples/from_json__default_constructible.cpp similarity index 93% rename from docs/examples/from_json__default_constructible.cpp rename to docs/mkdocs/docs/examples/from_json__default_constructible.cpp index 07d71ac951..17c0551c8c 100644 --- a/docs/examples/from_json__default_constructible.cpp +++ b/docs/mkdocs/docs/examples/from_json__default_constructible.cpp @@ -31,7 +31,7 @@ int main() j["address"] = "744 Evergreen Terrace"; j["age"] = 60; - auto p = j.template get(); + auto p = j.get(); std::cout << p.name << " (" << p.age << ") lives in " << p.address << std::endl; } diff --git a/docs/examples/from_json__default_constructible.output b/docs/mkdocs/docs/examples/from_json__default_constructible.output similarity index 100% rename from docs/examples/from_json__default_constructible.output rename to docs/mkdocs/docs/examples/from_json__default_constructible.output diff --git a/docs/examples/from_json__non_default_constructible.cpp b/docs/mkdocs/docs/examples/from_json__non_default_constructible.cpp similarity index 96% rename from docs/examples/from_json__non_default_constructible.cpp rename to docs/mkdocs/docs/examples/from_json__non_default_constructible.cpp index ec8206eade..6cb86153cc 100644 --- a/docs/examples/from_json__non_default_constructible.cpp +++ b/docs/mkdocs/docs/examples/from_json__non_default_constructible.cpp @@ -47,7 +47,7 @@ int main() j["address"] = "744 Evergreen Terrace"; j["age"] = 60; - auto p = j.template get(); + auto p = j.get(); std::cout << p.name << " (" << p.age << ") lives in " << p.address << std::endl; } diff --git a/docs/examples/from_json__non_default_constructible.output b/docs/mkdocs/docs/examples/from_json__non_default_constructible.output similarity index 100% rename from docs/examples/from_json__non_default_constructible.output rename to docs/mkdocs/docs/examples/from_json__non_default_constructible.output diff --git a/docs/examples/from_msgpack.cpp b/docs/mkdocs/docs/examples/from_msgpack.cpp similarity index 100% rename from docs/examples/from_msgpack.cpp rename to docs/mkdocs/docs/examples/from_msgpack.cpp diff --git a/docs/examples/from_msgpack.output b/docs/mkdocs/docs/examples/from_msgpack.output similarity index 100% rename from docs/examples/from_msgpack.output rename to docs/mkdocs/docs/examples/from_msgpack.output diff --git a/docs/examples/from_ubjson.cpp b/docs/mkdocs/docs/examples/from_ubjson.cpp similarity index 100% rename from docs/examples/from_ubjson.cpp rename to docs/mkdocs/docs/examples/from_ubjson.cpp diff --git a/docs/examples/from_ubjson.output b/docs/mkdocs/docs/examples/from_ubjson.output similarity index 100% rename from docs/examples/from_ubjson.output rename to docs/mkdocs/docs/examples/from_ubjson.output diff --git a/docs/examples/front.cpp b/docs/mkdocs/docs/examples/front.cpp similarity index 100% rename from docs/examples/front.cpp rename to docs/mkdocs/docs/examples/front.cpp diff --git a/docs/examples/front.output b/docs/mkdocs/docs/examples/front.output similarity index 100% rename from docs/examples/front.output rename to docs/mkdocs/docs/examples/front.output diff --git a/docs/examples/get__PointerType.cpp b/docs/mkdocs/docs/examples/get__PointerType.cpp similarity index 50% rename from docs/examples/get__PointerType.cpp rename to docs/mkdocs/docs/examples/get__PointerType.cpp index 309c8deebb..2f32ed7af6 100644 --- a/docs/examples/get__PointerType.cpp +++ b/docs/mkdocs/docs/examples/get__PointerType.cpp @@ -9,11 +9,11 @@ int main() json value = 17; // explicitly getting pointers - auto p1 = value.template get(); - auto p2 = value.template get(); - auto p3 = value.template get(); - auto p4 = value.template get(); - auto p5 = value.template get(); + auto p1 = value.get(); + auto p2 = value.get(); + auto p3 = value.get(); + auto p4 = value.get(); + auto p5 = value.get(); // print the pointees std::cout << *p1 << ' ' << *p2 << ' ' << *p3 << ' ' << *p4 << '\n'; diff --git a/docs/examples/get__PointerType.output b/docs/mkdocs/docs/examples/get__PointerType.output similarity index 100% rename from docs/examples/get__PointerType.output rename to docs/mkdocs/docs/examples/get__PointerType.output diff --git a/docs/examples/get__ValueType_const.cpp b/docs/mkdocs/docs/examples/get__ValueType_const.cpp similarity index 60% rename from docs/examples/get__ValueType_const.cpp rename to docs/mkdocs/docs/examples/get__ValueType_const.cpp index db63791fcb..7a703aaeb1 100644 --- a/docs/examples/get__ValueType_const.cpp +++ b/docs/mkdocs/docs/examples/get__ValueType_const.cpp @@ -22,14 +22,14 @@ int main() }; // use explicit conversions - auto v1 = json_types["boolean"].template get(); - auto v2 = json_types["number"]["integer"].template get(); - auto v3 = json_types["number"]["integer"].template get(); - auto v4 = json_types["number"]["floating-point"].template get(); - auto v5 = json_types["number"]["floating-point"].template get(); - auto v6 = json_types["string"].template get(); - auto v7 = json_types["array"].template get>(); - auto v8 = json_types.template get>(); + auto v1 = json_types["boolean"].get(); + auto v2 = json_types["number"]["integer"].get(); + auto v3 = json_types["number"]["integer"].get(); + auto v4 = json_types["number"]["floating-point"].get(); + auto v5 = json_types["number"]["floating-point"].get(); + auto v6 = json_types["string"].get(); + auto v7 = json_types["array"].get>(); + auto v8 = json_types.get>(); // print the conversion results std::cout << v1 << '\n'; diff --git a/docs/examples/get__ValueType_const.output b/docs/mkdocs/docs/examples/get__ValueType_const.output similarity index 100% rename from docs/examples/get__ValueType_const.output rename to docs/mkdocs/docs/examples/get__ValueType_const.output diff --git a/docs/examples/get_allocator.cpp b/docs/mkdocs/docs/examples/get_allocator.cpp similarity index 100% rename from docs/examples/get_allocator.cpp rename to docs/mkdocs/docs/examples/get_allocator.cpp diff --git a/docs/examples/get_allocator.output b/docs/mkdocs/docs/examples/get_allocator.output similarity index 100% rename from docs/examples/get_allocator.output rename to docs/mkdocs/docs/examples/get_allocator.output diff --git a/docs/examples/get_binary.cpp b/docs/mkdocs/docs/examples/get_binary.cpp similarity index 100% rename from docs/examples/get_binary.cpp rename to docs/mkdocs/docs/examples/get_binary.cpp diff --git a/docs/examples/get_binary.output b/docs/mkdocs/docs/examples/get_binary.output similarity index 100% rename from docs/examples/get_binary.output rename to docs/mkdocs/docs/examples/get_binary.output diff --git a/docs/examples/get_ptr.cpp b/docs/mkdocs/docs/examples/get_ptr.cpp similarity index 100% rename from docs/examples/get_ptr.cpp rename to docs/mkdocs/docs/examples/get_ptr.cpp diff --git a/docs/examples/get_ptr.output b/docs/mkdocs/docs/examples/get_ptr.output similarity index 100% rename from docs/examples/get_ptr.output rename to docs/mkdocs/docs/examples/get_ptr.output diff --git a/docs/examples/get_ref.cpp b/docs/mkdocs/docs/examples/get_ref.cpp similarity index 100% rename from docs/examples/get_ref.cpp rename to docs/mkdocs/docs/examples/get_ref.cpp diff --git a/docs/examples/get_ref.output b/docs/mkdocs/docs/examples/get_ref.output similarity index 100% rename from docs/examples/get_ref.output rename to docs/mkdocs/docs/examples/get_ref.output diff --git a/docs/examples/get_to.cpp b/docs/mkdocs/docs/examples/get_to.cpp similarity index 100% rename from docs/examples/get_to.cpp rename to docs/mkdocs/docs/examples/get_to.cpp diff --git a/docs/examples/get_to.output b/docs/mkdocs/docs/examples/get_to.output similarity index 100% rename from docs/examples/get_to.output rename to docs/mkdocs/docs/examples/get_to.output diff --git a/docs/examples/insert.cpp b/docs/mkdocs/docs/examples/insert.cpp similarity index 100% rename from docs/examples/insert.cpp rename to docs/mkdocs/docs/examples/insert.cpp diff --git a/docs/examples/insert.output b/docs/mkdocs/docs/examples/insert.output similarity index 100% rename from docs/examples/insert.output rename to docs/mkdocs/docs/examples/insert.output diff --git a/docs/examples/insert__count.cpp b/docs/mkdocs/docs/examples/insert__count.cpp similarity index 100% rename from docs/examples/insert__count.cpp rename to docs/mkdocs/docs/examples/insert__count.cpp diff --git a/docs/examples/insert__count.output b/docs/mkdocs/docs/examples/insert__count.output similarity index 100% rename from docs/examples/insert__count.output rename to docs/mkdocs/docs/examples/insert__count.output diff --git a/docs/examples/insert__ilist.cpp b/docs/mkdocs/docs/examples/insert__ilist.cpp similarity index 100% rename from docs/examples/insert__ilist.cpp rename to docs/mkdocs/docs/examples/insert__ilist.cpp diff --git a/docs/examples/insert__ilist.output b/docs/mkdocs/docs/examples/insert__ilist.output similarity index 100% rename from docs/examples/insert__ilist.output rename to docs/mkdocs/docs/examples/insert__ilist.output diff --git a/docs/examples/insert__range.cpp b/docs/mkdocs/docs/examples/insert__range.cpp similarity index 100% rename from docs/examples/insert__range.cpp rename to docs/mkdocs/docs/examples/insert__range.cpp diff --git a/docs/examples/insert__range.output b/docs/mkdocs/docs/examples/insert__range.output similarity index 100% rename from docs/examples/insert__range.output rename to docs/mkdocs/docs/examples/insert__range.output diff --git a/docs/examples/insert__range_object.cpp b/docs/mkdocs/docs/examples/insert__range_object.cpp similarity index 100% rename from docs/examples/insert__range_object.cpp rename to docs/mkdocs/docs/examples/insert__range_object.cpp diff --git a/docs/examples/insert__range_object.output b/docs/mkdocs/docs/examples/insert__range_object.output similarity index 100% rename from docs/examples/insert__range_object.output rename to docs/mkdocs/docs/examples/insert__range_object.output diff --git a/docs/examples/invalid_iterator.cpp b/docs/mkdocs/docs/examples/invalid_iterator.cpp similarity index 100% rename from docs/examples/invalid_iterator.cpp rename to docs/mkdocs/docs/examples/invalid_iterator.cpp diff --git a/docs/examples/invalid_iterator.output b/docs/mkdocs/docs/examples/invalid_iterator.output similarity index 100% rename from docs/examples/invalid_iterator.output rename to docs/mkdocs/docs/examples/invalid_iterator.output diff --git a/docs/examples/is_array.cpp b/docs/mkdocs/docs/examples/is_array.cpp similarity index 100% rename from docs/examples/is_array.cpp rename to docs/mkdocs/docs/examples/is_array.cpp diff --git a/docs/examples/is_array.output b/docs/mkdocs/docs/examples/is_array.output similarity index 100% rename from docs/examples/is_array.output rename to docs/mkdocs/docs/examples/is_array.output diff --git a/docs/examples/is_binary.cpp b/docs/mkdocs/docs/examples/is_binary.cpp similarity index 100% rename from docs/examples/is_binary.cpp rename to docs/mkdocs/docs/examples/is_binary.cpp diff --git a/docs/examples/is_binary.output b/docs/mkdocs/docs/examples/is_binary.output similarity index 100% rename from docs/examples/is_binary.output rename to docs/mkdocs/docs/examples/is_binary.output diff --git a/docs/examples/is_boolean.cpp b/docs/mkdocs/docs/examples/is_boolean.cpp similarity index 100% rename from docs/examples/is_boolean.cpp rename to docs/mkdocs/docs/examples/is_boolean.cpp diff --git a/docs/examples/is_boolean.output b/docs/mkdocs/docs/examples/is_boolean.output similarity index 100% rename from docs/examples/is_boolean.output rename to docs/mkdocs/docs/examples/is_boolean.output diff --git a/docs/examples/is_discarded.cpp b/docs/mkdocs/docs/examples/is_discarded.cpp similarity index 100% rename from docs/examples/is_discarded.cpp rename to docs/mkdocs/docs/examples/is_discarded.cpp diff --git a/docs/examples/is_discarded.output b/docs/mkdocs/docs/examples/is_discarded.output similarity index 100% rename from docs/examples/is_discarded.output rename to docs/mkdocs/docs/examples/is_discarded.output diff --git a/docs/examples/is_null.cpp b/docs/mkdocs/docs/examples/is_null.cpp similarity index 100% rename from docs/examples/is_null.cpp rename to docs/mkdocs/docs/examples/is_null.cpp diff --git a/docs/examples/is_null.output b/docs/mkdocs/docs/examples/is_null.output similarity index 100% rename from docs/examples/is_null.output rename to docs/mkdocs/docs/examples/is_null.output diff --git a/docs/examples/is_number.cpp b/docs/mkdocs/docs/examples/is_number.cpp similarity index 100% rename from docs/examples/is_number.cpp rename to docs/mkdocs/docs/examples/is_number.cpp diff --git a/docs/examples/is_number.output b/docs/mkdocs/docs/examples/is_number.output similarity index 100% rename from docs/examples/is_number.output rename to docs/mkdocs/docs/examples/is_number.output diff --git a/docs/examples/is_number_float.cpp b/docs/mkdocs/docs/examples/is_number_float.cpp similarity index 100% rename from docs/examples/is_number_float.cpp rename to docs/mkdocs/docs/examples/is_number_float.cpp diff --git a/docs/examples/is_number_float.output b/docs/mkdocs/docs/examples/is_number_float.output similarity index 100% rename from docs/examples/is_number_float.output rename to docs/mkdocs/docs/examples/is_number_float.output diff --git a/docs/examples/is_number_integer.cpp b/docs/mkdocs/docs/examples/is_number_integer.cpp similarity index 100% rename from docs/examples/is_number_integer.cpp rename to docs/mkdocs/docs/examples/is_number_integer.cpp diff --git a/docs/examples/is_number_integer.output b/docs/mkdocs/docs/examples/is_number_integer.output similarity index 100% rename from docs/examples/is_number_integer.output rename to docs/mkdocs/docs/examples/is_number_integer.output diff --git a/docs/examples/is_number_unsigned.cpp b/docs/mkdocs/docs/examples/is_number_unsigned.cpp similarity index 100% rename from docs/examples/is_number_unsigned.cpp rename to docs/mkdocs/docs/examples/is_number_unsigned.cpp diff --git a/docs/examples/is_number_unsigned.output b/docs/mkdocs/docs/examples/is_number_unsigned.output similarity index 100% rename from docs/examples/is_number_unsigned.output rename to docs/mkdocs/docs/examples/is_number_unsigned.output diff --git a/docs/examples/is_object.cpp b/docs/mkdocs/docs/examples/is_object.cpp similarity index 100% rename from docs/examples/is_object.cpp rename to docs/mkdocs/docs/examples/is_object.cpp diff --git a/docs/examples/is_object.output b/docs/mkdocs/docs/examples/is_object.output similarity index 100% rename from docs/examples/is_object.output rename to docs/mkdocs/docs/examples/is_object.output diff --git a/docs/examples/is_primitive.cpp b/docs/mkdocs/docs/examples/is_primitive.cpp similarity index 100% rename from docs/examples/is_primitive.cpp rename to docs/mkdocs/docs/examples/is_primitive.cpp diff --git a/docs/examples/is_primitive.output b/docs/mkdocs/docs/examples/is_primitive.output similarity index 100% rename from docs/examples/is_primitive.output rename to docs/mkdocs/docs/examples/is_primitive.output diff --git a/docs/examples/is_string.cpp b/docs/mkdocs/docs/examples/is_string.cpp similarity index 100% rename from docs/examples/is_string.cpp rename to docs/mkdocs/docs/examples/is_string.cpp diff --git a/docs/examples/is_string.output b/docs/mkdocs/docs/examples/is_string.output similarity index 100% rename from docs/examples/is_string.output rename to docs/mkdocs/docs/examples/is_string.output diff --git a/docs/examples/is_structured.cpp b/docs/mkdocs/docs/examples/is_structured.cpp similarity index 100% rename from docs/examples/is_structured.cpp rename to docs/mkdocs/docs/examples/is_structured.cpp diff --git a/docs/examples/is_structured.output b/docs/mkdocs/docs/examples/is_structured.output similarity index 100% rename from docs/examples/is_structured.output rename to docs/mkdocs/docs/examples/is_structured.output diff --git a/docs/examples/items.cpp b/docs/mkdocs/docs/examples/items.cpp similarity index 100% rename from docs/examples/items.cpp rename to docs/mkdocs/docs/examples/items.cpp diff --git a/docs/examples/items.output b/docs/mkdocs/docs/examples/items.output similarity index 100% rename from docs/examples/items.output rename to docs/mkdocs/docs/examples/items.output diff --git a/docs/examples/json_base_class_t.cpp b/docs/mkdocs/docs/examples/json_base_class_t.cpp similarity index 96% rename from docs/examples/json_base_class_t.cpp rename to docs/mkdocs/docs/examples/json_base_class_t.cpp index d993522a70..3fb2d46a27 100644 --- a/docs/examples/json_base_class_t.cpp +++ b/docs/mkdocs/docs/examples/json_base_class_t.cpp @@ -79,8 +79,8 @@ int main() // visit and output j.visit( - [&](const json::json_pointer & p, - const json & j) + [&](const json::json_pointer & p, + const json & j) { std::cout << (p.empty() ? std::string{"/"} : p.to_string()) << " - metadata = " << j.metadata << " -> " << j.dump() << '\n'; diff --git a/docs/examples/json_base_class_t.output b/docs/mkdocs/docs/examples/json_base_class_t.output similarity index 100% rename from docs/examples/json_base_class_t.output rename to docs/mkdocs/docs/examples/json_base_class_t.output diff --git a/docs/examples/json_lines.cpp b/docs/mkdocs/docs/examples/json_lines.cpp similarity index 100% rename from docs/examples/json_lines.cpp rename to docs/mkdocs/docs/examples/json_lines.cpp diff --git a/docs/examples/json_lines.output b/docs/mkdocs/docs/examples/json_lines.output similarity index 100% rename from docs/examples/json_lines.output rename to docs/mkdocs/docs/examples/json_lines.output diff --git a/docs/examples/json_pointer.cpp b/docs/mkdocs/docs/examples/json_pointer.cpp similarity index 100% rename from docs/examples/json_pointer.cpp rename to docs/mkdocs/docs/examples/json_pointer.cpp diff --git a/docs/examples/json_pointer.output b/docs/mkdocs/docs/examples/json_pointer.output similarity index 100% rename from docs/examples/json_pointer.output rename to docs/mkdocs/docs/examples/json_pointer.output diff --git a/docs/examples/json_pointer__back.cpp b/docs/mkdocs/docs/examples/json_pointer__back.cpp similarity index 100% rename from docs/examples/json_pointer__back.cpp rename to docs/mkdocs/docs/examples/json_pointer__back.cpp diff --git a/docs/examples/json_pointer__back.output b/docs/mkdocs/docs/examples/json_pointer__back.output similarity index 100% rename from docs/examples/json_pointer__back.output rename to docs/mkdocs/docs/examples/json_pointer__back.output diff --git a/docs/examples/json_pointer__empty.cpp b/docs/mkdocs/docs/examples/json_pointer__empty.cpp similarity index 100% rename from docs/examples/json_pointer__empty.cpp rename to docs/mkdocs/docs/examples/json_pointer__empty.cpp diff --git a/docs/examples/json_pointer__empty.output b/docs/mkdocs/docs/examples/json_pointer__empty.output similarity index 100% rename from docs/examples/json_pointer__empty.output rename to docs/mkdocs/docs/examples/json_pointer__empty.output diff --git a/docs/examples/json_pointer__operator__equal.cpp b/docs/mkdocs/docs/examples/json_pointer__operator__equal.cpp similarity index 100% rename from docs/examples/json_pointer__operator__equal.cpp rename to docs/mkdocs/docs/examples/json_pointer__operator__equal.cpp diff --git a/docs/examples/json_pointer__operator__equal.output b/docs/mkdocs/docs/examples/json_pointer__operator__equal.output similarity index 100% rename from docs/examples/json_pointer__operator__equal.output rename to docs/mkdocs/docs/examples/json_pointer__operator__equal.output diff --git a/docs/examples/json_pointer__operator__equal_stringtype.cpp b/docs/mkdocs/docs/examples/json_pointer__operator__equal_stringtype.cpp similarity index 100% rename from docs/examples/json_pointer__operator__equal_stringtype.cpp rename to docs/mkdocs/docs/examples/json_pointer__operator__equal_stringtype.cpp diff --git a/docs/examples/json_pointer__operator__equal_stringtype.output b/docs/mkdocs/docs/examples/json_pointer__operator__equal_stringtype.output similarity index 100% rename from docs/examples/json_pointer__operator__equal_stringtype.output rename to docs/mkdocs/docs/examples/json_pointer__operator__equal_stringtype.output diff --git a/docs/examples/json_pointer__operator__notequal.cpp b/docs/mkdocs/docs/examples/json_pointer__operator__notequal.cpp similarity index 100% rename from docs/examples/json_pointer__operator__notequal.cpp rename to docs/mkdocs/docs/examples/json_pointer__operator__notequal.cpp diff --git a/docs/examples/json_pointer__operator__notequal.output b/docs/mkdocs/docs/examples/json_pointer__operator__notequal.output similarity index 100% rename from docs/examples/json_pointer__operator__notequal.output rename to docs/mkdocs/docs/examples/json_pointer__operator__notequal.output diff --git a/docs/examples/json_pointer__operator__notequal_stringtype.cpp b/docs/mkdocs/docs/examples/json_pointer__operator__notequal_stringtype.cpp similarity index 100% rename from docs/examples/json_pointer__operator__notequal_stringtype.cpp rename to docs/mkdocs/docs/examples/json_pointer__operator__notequal_stringtype.cpp diff --git a/docs/examples/json_pointer__operator__notequal_stringtype.output b/docs/mkdocs/docs/examples/json_pointer__operator__notequal_stringtype.output similarity index 100% rename from docs/examples/json_pointer__operator__notequal_stringtype.output rename to docs/mkdocs/docs/examples/json_pointer__operator__notequal_stringtype.output diff --git a/docs/examples/json_pointer__operator_add.cpp b/docs/mkdocs/docs/examples/json_pointer__operator_add.cpp similarity index 100% rename from docs/examples/json_pointer__operator_add.cpp rename to docs/mkdocs/docs/examples/json_pointer__operator_add.cpp diff --git a/docs/examples/json_pointer__operator_add.output b/docs/mkdocs/docs/examples/json_pointer__operator_add.output similarity index 100% rename from docs/examples/json_pointer__operator_add.output rename to docs/mkdocs/docs/examples/json_pointer__operator_add.output diff --git a/docs/examples/json_pointer__operator_add_binary.cpp b/docs/mkdocs/docs/examples/json_pointer__operator_add_binary.cpp similarity index 100% rename from docs/examples/json_pointer__operator_add_binary.cpp rename to docs/mkdocs/docs/examples/json_pointer__operator_add_binary.cpp diff --git a/docs/examples/json_pointer__operator_add_binary.output b/docs/mkdocs/docs/examples/json_pointer__operator_add_binary.output similarity index 100% rename from docs/examples/json_pointer__operator_add_binary.output rename to docs/mkdocs/docs/examples/json_pointer__operator_add_binary.output diff --git a/docs/examples/json_pointer__operator_string_t.cpp b/docs/mkdocs/docs/examples/json_pointer__operator_string_t.cpp similarity index 100% rename from docs/examples/json_pointer__operator_string_t.cpp rename to docs/mkdocs/docs/examples/json_pointer__operator_string_t.cpp diff --git a/docs/examples/json_pointer__operator_string_t.output b/docs/mkdocs/docs/examples/json_pointer__operator_string_t.output similarity index 100% rename from docs/examples/json_pointer__operator_string_t.output rename to docs/mkdocs/docs/examples/json_pointer__operator_string_t.output diff --git a/docs/examples/json_pointer__parent_pointer.cpp b/docs/mkdocs/docs/examples/json_pointer__parent_pointer.cpp similarity index 100% rename from docs/examples/json_pointer__parent_pointer.cpp rename to docs/mkdocs/docs/examples/json_pointer__parent_pointer.cpp diff --git a/docs/examples/json_pointer__parent_pointer.output b/docs/mkdocs/docs/examples/json_pointer__parent_pointer.output similarity index 100% rename from docs/examples/json_pointer__parent_pointer.output rename to docs/mkdocs/docs/examples/json_pointer__parent_pointer.output diff --git a/docs/examples/json_pointer__pop_back.cpp b/docs/mkdocs/docs/examples/json_pointer__pop_back.cpp similarity index 100% rename from docs/examples/json_pointer__pop_back.cpp rename to docs/mkdocs/docs/examples/json_pointer__pop_back.cpp diff --git a/docs/examples/json_pointer__pop_back.output b/docs/mkdocs/docs/examples/json_pointer__pop_back.output similarity index 100% rename from docs/examples/json_pointer__pop_back.output rename to docs/mkdocs/docs/examples/json_pointer__pop_back.output diff --git a/docs/examples/json_pointer__push_back.cpp b/docs/mkdocs/docs/examples/json_pointer__push_back.cpp similarity index 100% rename from docs/examples/json_pointer__push_back.cpp rename to docs/mkdocs/docs/examples/json_pointer__push_back.cpp diff --git a/docs/examples/json_pointer__push_back.output b/docs/mkdocs/docs/examples/json_pointer__push_back.output similarity index 100% rename from docs/examples/json_pointer__push_back.output rename to docs/mkdocs/docs/examples/json_pointer__push_back.output diff --git a/docs/examples/json_pointer__string_t.cpp b/docs/mkdocs/docs/examples/json_pointer__string_t.cpp similarity index 100% rename from docs/examples/json_pointer__string_t.cpp rename to docs/mkdocs/docs/examples/json_pointer__string_t.cpp diff --git a/docs/examples/json_pointer__string_t.output b/docs/mkdocs/docs/examples/json_pointer__string_t.output similarity index 100% rename from docs/examples/json_pointer__string_t.output rename to docs/mkdocs/docs/examples/json_pointer__string_t.output diff --git a/docs/examples/json_pointer__to_string.cpp b/docs/mkdocs/docs/examples/json_pointer__to_string.cpp similarity index 100% rename from docs/examples/json_pointer__to_string.cpp rename to docs/mkdocs/docs/examples/json_pointer__to_string.cpp diff --git a/docs/examples/json_pointer__to_string.output b/docs/mkdocs/docs/examples/json_pointer__to_string.output similarity index 100% rename from docs/examples/json_pointer__to_string.output rename to docs/mkdocs/docs/examples/json_pointer__to_string.output diff --git a/docs/examples/max_size.cpp b/docs/mkdocs/docs/examples/max_size.cpp similarity index 100% rename from docs/examples/max_size.cpp rename to docs/mkdocs/docs/examples/max_size.cpp diff --git a/docs/examples/max_size.output b/docs/mkdocs/docs/examples/max_size.output similarity index 100% rename from docs/examples/max_size.output rename to docs/mkdocs/docs/examples/max_size.output diff --git a/docs/examples/merge_patch.cpp b/docs/mkdocs/docs/examples/merge_patch.cpp similarity index 100% rename from docs/examples/merge_patch.cpp rename to docs/mkdocs/docs/examples/merge_patch.cpp diff --git a/docs/examples/merge_patch.output b/docs/mkdocs/docs/examples/merge_patch.output similarity index 100% rename from docs/examples/merge_patch.output rename to docs/mkdocs/docs/examples/merge_patch.output diff --git a/docs/examples/meta.cpp b/docs/mkdocs/docs/examples/meta.cpp similarity index 100% rename from docs/examples/meta.cpp rename to docs/mkdocs/docs/examples/meta.cpp diff --git a/docs/examples/meta.output b/docs/mkdocs/docs/examples/meta.output similarity index 61% rename from docs/examples/meta.output rename to docs/mkdocs/docs/examples/meta.output index 90390f96bd..b6414adb3b 100644 --- a/docs/examples/meta.output +++ b/docs/mkdocs/docs/examples/meta.output @@ -2,16 +2,16 @@ "compiler": { "c++": "201103", "family": "gcc", - "version": "12.3.0" + "version": "12.4.0" }, - "copyright": "(C) 2013-2022 Niels Lohmann", + "copyright": "(C) 2013-2026 Niels Lohmann", "name": "JSON for Modern C++", "platform": "apple", "url": "https://github.com/nlohmann/json", "version": { "major": 3, - "minor": 11, - "patch": 3, - "string": "3.11.3" + "minor": 12, + "patch": 0, + "string": "3.12.0" } } diff --git a/docs/mkdocs/docs/examples/nlohmann_define_derived_type_intrusive_macro.cpp b/docs/mkdocs/docs/examples/nlohmann_define_derived_type_intrusive_macro.cpp new file mode 100644 index 0000000000..0dca781629 --- /dev/null +++ b/docs/mkdocs/docs/examples/nlohmann_define_derived_type_intrusive_macro.cpp @@ -0,0 +1,37 @@ +#include +#include +#include + +using nlohmann::json; + +class A +{ + private: + double Aa = 0.0; + double Ab = 0.0; + + public: + A() = default; + A(double a, double b) : Aa(a), Ab(b) {} + NLOHMANN_DEFINE_TYPE_INTRUSIVE(A, Aa, Ab) +}; + +class B : public A +{ + private: + int Ba = 0; + int Bb = 0; + + public: + B() = default; + B(int a, int b, double aa, double ab) : A(aa, ab), Ba(a), Bb(b) {} + NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(B, A, Ba, Bb) +}; + +int main() +{ + B example(23, 42, 3.142, 1.777); + json example_json = example; + + std::cout << std::setw(4) << example_json << std::endl; +} \ No newline at end of file diff --git a/docs/mkdocs/docs/examples/nlohmann_define_derived_type_intrusive_macro.output b/docs/mkdocs/docs/examples/nlohmann_define_derived_type_intrusive_macro.output new file mode 100644 index 0000000000..0f97be4a03 --- /dev/null +++ b/docs/mkdocs/docs/examples/nlohmann_define_derived_type_intrusive_macro.output @@ -0,0 +1,6 @@ +{ + "Aa": 3.142, + "Ab": 1.777, + "Ba": 23, + "Bb": 42 +} diff --git a/docs/examples/nlohmann_define_type_intrusive_explicit.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_explicit.cpp similarity index 81% rename from docs/examples/nlohmann_define_type_intrusive_explicit.cpp rename to docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_explicit.cpp index de79bd37c9..ce13ff114f 100644 --- a/docs/examples/nlohmann_define_type_intrusive_explicit.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_explicit.cpp @@ -19,14 +19,16 @@ class person : name(std::move(name_)), address(std::move(address_)), age(age_) {} - friend void to_json(nlohmann::json& nlohmann_json_j, const person& nlohmann_json_t) + template + friend void to_json(BasicJsonType& nlohmann_json_j, const person& nlohmann_json_t) { nlohmann_json_j["name"] = nlohmann_json_t.name; nlohmann_json_j["address"] = nlohmann_json_t.address; nlohmann_json_j["age"] = nlohmann_json_t.age; } - friend void from_json(const nlohmann::json& nlohmann_json_j, person& nlohmann_json_t) + template + friend void from_json(const BasicJsonType& nlohmann_json_j, person& nlohmann_json_t) { nlohmann_json_t.name = nlohmann_json_j.at("name"); nlohmann_json_t.address = nlohmann_json_j.at("address"); @@ -45,13 +47,13 @@ int main() // deserialization: json -> person json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; - auto p2 = j2.template get(); + auto p2 = j2.get(); // incomplete deserialization: json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json; try { - auto p3 = j3.template get(); + auto p3 = j3.get(); } catch (const json::exception& e) { diff --git a/docs/examples/nlohmann_define_type_intrusive_explicit.output b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_explicit.output similarity index 100% rename from docs/examples/nlohmann_define_type_intrusive_explicit.output rename to docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_explicit.output diff --git a/docs/examples/nlohmann_define_type_intrusive_macro.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_macro.cpp similarity index 92% rename from docs/examples/nlohmann_define_type_intrusive_macro.cpp rename to docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_macro.cpp index 4ecd4294fc..ab8657801b 100644 --- a/docs/examples/nlohmann_define_type_intrusive_macro.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_macro.cpp @@ -33,13 +33,13 @@ int main() // deserialization: json -> person json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; - auto p2 = j2.template get(); + auto p2 = j2.get(); // incomplete deserialization: json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json; try { - auto p3 = j3.template get(); + auto p3 = j3.get(); } catch (const json::exception& e) { diff --git a/docs/examples/nlohmann_define_type_intrusive_macro.output b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_macro.output similarity index 100% rename from docs/examples/nlohmann_define_type_intrusive_macro.output rename to docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_macro.output diff --git a/docs/examples/nlohmann_define_type_intrusive_only_serialize_explicit.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_only_serialize_explicit.cpp similarity index 87% rename from docs/examples/nlohmann_define_type_intrusive_only_serialize_explicit.cpp rename to docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_only_serialize_explicit.cpp index ea422beb5e..dd6a9a6792 100644 --- a/docs/examples/nlohmann_define_type_intrusive_only_serialize_explicit.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_only_serialize_explicit.cpp @@ -19,7 +19,8 @@ class person : name(std::move(name_)), address(std::move(address_)), age(age_) {} - friend void to_json(nlohmann::json& nlohmann_json_j, const person& nlohmann_json_t) + template + friend void to_json(BasicJsonType& nlohmann_json_j, const person& nlohmann_json_t) { nlohmann_json_j["name"] = nlohmann_json_t.name; nlohmann_json_j["address"] = nlohmann_json_t.address; diff --git a/docs/examples/nlohmann_define_type_intrusive_only_serialize_explicit.output b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_only_serialize_explicit.output similarity index 100% rename from docs/examples/nlohmann_define_type_intrusive_only_serialize_explicit.output rename to docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_only_serialize_explicit.output diff --git a/docs/examples/nlohmann_define_type_intrusive_only_serialize_macro.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_only_serialize_macro.cpp similarity index 100% rename from docs/examples/nlohmann_define_type_intrusive_only_serialize_macro.cpp rename to docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_only_serialize_macro.cpp diff --git a/docs/examples/nlohmann_define_type_intrusive_only_serialize_macro.output b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_only_serialize_macro.output similarity index 100% rename from docs/examples/nlohmann_define_type_intrusive_only_serialize_macro.output rename to docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_only_serialize_macro.output diff --git a/docs/examples/nlohmann_define_type_intrusive_with_default_explicit.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_with_default_explicit.cpp similarity index 82% rename from docs/examples/nlohmann_define_type_intrusive_with_default_explicit.cpp rename to docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_with_default_explicit.cpp index 0fb2e1cb21..2713444b62 100644 --- a/docs/examples/nlohmann_define_type_intrusive_with_default_explicit.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_with_default_explicit.cpp @@ -19,14 +19,16 @@ class person : name(std::move(name_)), address(std::move(address_)), age(age_) {} - friend void to_json(nlohmann::json& nlohmann_json_j, const person& nlohmann_json_t) + template + friend void to_json(BasicJsonType& nlohmann_json_j, const person& nlohmann_json_t) { nlohmann_json_j["name"] = nlohmann_json_t.name; nlohmann_json_j["address"] = nlohmann_json_t.address; nlohmann_json_j["age"] = nlohmann_json_t.age; } - friend void from_json(const nlohmann::json& nlohmann_json_j, person& nlohmann_json_t) + template + friend void from_json(const BasicJsonType& nlohmann_json_j, person& nlohmann_json_t) { person nlohmann_json_default_obj; nlohmann_json_t.name = nlohmann_json_j.value("name", nlohmann_json_default_obj.name); @@ -46,10 +48,10 @@ int main() // deserialization: json -> person json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; - auto p2 = j2.template get(); + auto p2 = j2.get(); // incomplete deserialization: json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json; - auto p3 = j3.template get(); + auto p3 = j3.get(); std::cout << "roundtrip: " << json(p3) << std::endl; } diff --git a/docs/examples/nlohmann_define_type_intrusive_with_default_explicit.output b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_with_default_explicit.output similarity index 100% rename from docs/examples/nlohmann_define_type_intrusive_with_default_explicit.output rename to docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_with_default_explicit.output diff --git a/docs/examples/nlohmann_define_type_intrusive_with_default_macro.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_with_default_macro.cpp similarity index 92% rename from docs/examples/nlohmann_define_type_intrusive_with_default_macro.cpp rename to docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_with_default_macro.cpp index 52668516b5..851a3582f4 100644 --- a/docs/examples/nlohmann_define_type_intrusive_with_default_macro.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_with_default_macro.cpp @@ -33,10 +33,10 @@ int main() // deserialization: json -> person json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; - auto p2 = j2.template get(); + auto p2 = j2.get(); // incomplete deserialization: json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json; - auto p3 = j3.template get(); + auto p3 = j3.get(); std::cout << "roundtrip: " << json(p3) << std::endl; } diff --git a/docs/examples/nlohmann_define_type_intrusive_with_default_macro.output b/docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_with_default_macro.output similarity index 100% rename from docs/examples/nlohmann_define_type_intrusive_with_default_macro.output rename to docs/mkdocs/docs/examples/nlohmann_define_type_intrusive_with_default_macro.output diff --git a/docs/examples/nlohmann_define_type_non_intrusive_explicit.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_explicit.cpp similarity index 79% rename from docs/examples/nlohmann_define_type_non_intrusive_explicit.cpp rename to docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_explicit.cpp index a31a7eb8ff..dfbdde4ac2 100644 --- a/docs/examples/nlohmann_define_type_non_intrusive_explicit.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_explicit.cpp @@ -13,14 +13,16 @@ struct person int age; }; -void to_json(nlohmann::json& nlohmann_json_j, const person& nlohmann_json_t) +template +void to_json(BasicJsonType& nlohmann_json_j, const person& nlohmann_json_t) { nlohmann_json_j["name"] = nlohmann_json_t.name; nlohmann_json_j["address"] = nlohmann_json_t.address; nlohmann_json_j["age"] = nlohmann_json_t.age; } -void from_json(const nlohmann::json& nlohmann_json_j, person& nlohmann_json_t) +template +void from_json(const BasicJsonType& nlohmann_json_j, person& nlohmann_json_t) { nlohmann_json_t.name = nlohmann_json_j.at("name"); nlohmann_json_t.address = nlohmann_json_j.at("address"); @@ -38,13 +40,13 @@ int main() // deserialization: json -> person json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; - auto p2 = j2.template get(); + auto p2 = j2.get(); // incomplete deserialization: json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json; try { - auto p3 = j3.template get(); + auto p3 = j3.get(); } catch (const json::exception& e) { diff --git a/docs/examples/nlohmann_define_type_non_intrusive_explicit.output b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_explicit.output similarity index 100% rename from docs/examples/nlohmann_define_type_non_intrusive_explicit.output rename to docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_explicit.output diff --git a/docs/examples/nlohmann_define_type_non_intrusive_macro.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_macro.cpp similarity index 90% rename from docs/examples/nlohmann_define_type_non_intrusive_macro.cpp rename to docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_macro.cpp index d11691b701..cdf90dc2c0 100644 --- a/docs/examples/nlohmann_define_type_non_intrusive_macro.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_macro.cpp @@ -26,13 +26,13 @@ int main() // deserialization: json -> person json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; - auto p2 = j2.template get(); + auto p2 = j2.get(); // incomplete deserialization: json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json; try { - auto p3 = j3.template get(); + auto p3 = j3.get(); } catch (const json::exception& e) { diff --git a/docs/examples/nlohmann_define_type_non_intrusive_macro.output b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_macro.output similarity index 100% rename from docs/examples/nlohmann_define_type_non_intrusive_macro.output rename to docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_macro.output diff --git a/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.cpp similarity index 84% rename from docs/examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.cpp rename to docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.cpp index 8890e03af5..ead75b74ec 100644 --- a/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.cpp @@ -13,7 +13,8 @@ struct person int age; }; -void to_json(nlohmann::json& nlohmann_json_j, const person& nlohmann_json_t) +template +void to_json(BasicJsonType& nlohmann_json_j, const person& nlohmann_json_t) { nlohmann_json_j["name"] = nlohmann_json_t.name; nlohmann_json_j["address"] = nlohmann_json_t.address; diff --git a/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.output b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.output similarity index 100% rename from docs/examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.output rename to docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.output diff --git a/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_macro.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_macro.cpp similarity index 100% rename from docs/examples/nlohmann_define_type_non_intrusive_only_serialize_macro.cpp rename to docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_macro.cpp diff --git a/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_macro.output b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_macro.output similarity index 100% rename from docs/examples/nlohmann_define_type_non_intrusive_only_serialize_macro.output rename to docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_macro.output diff --git a/docs/examples/nlohmann_define_type_non_intrusive_with_default_explicit.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_default_explicit.cpp similarity index 82% rename from docs/examples/nlohmann_define_type_non_intrusive_with_default_explicit.cpp rename to docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_default_explicit.cpp index 855ab52b85..23384b70cf 100644 --- a/docs/examples/nlohmann_define_type_non_intrusive_with_default_explicit.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_default_explicit.cpp @@ -18,14 +18,16 @@ struct person {} }; -void to_json(nlohmann::json& nlohmann_json_j, const person& nlohmann_json_t) +template +void to_json(BasicJsonType& nlohmann_json_j, const person& nlohmann_json_t) { nlohmann_json_j["name"] = nlohmann_json_t.name; nlohmann_json_j["address"] = nlohmann_json_t.address; nlohmann_json_j["age"] = nlohmann_json_t.age; } -void from_json(const nlohmann::json& nlohmann_json_j, person& nlohmann_json_t) +template +void from_json(const BasicJsonType& nlohmann_json_j, person& nlohmann_json_t) { person nlohmann_json_default_obj; nlohmann_json_t.name = nlohmann_json_j.value("name", nlohmann_json_default_obj.name); @@ -44,10 +46,10 @@ int main() // deserialization: json -> person json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; - auto p2 = j2.template get(); + auto p2 = j2.get(); // incomplete deserialization: json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json; - auto p3 = j3.template get(); + auto p3 = j3.get(); std::cout << "roundtrip: " << json(p3) << std::endl; } diff --git a/docs/examples/nlohmann_define_type_non_intrusive_with_default_explicit.output b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_default_explicit.output similarity index 100% rename from docs/examples/nlohmann_define_type_non_intrusive_with_default_explicit.output rename to docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_default_explicit.output diff --git a/docs/examples/nlohmann_define_type_non_intrusive_with_default_macro.cpp b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_default_macro.cpp similarity index 91% rename from docs/examples/nlohmann_define_type_non_intrusive_with_default_macro.cpp rename to docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_default_macro.cpp index 8c41c65ecd..aa9bc53b4b 100644 --- a/docs/examples/nlohmann_define_type_non_intrusive_with_default_macro.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_default_macro.cpp @@ -31,10 +31,10 @@ int main() // deserialization: json -> person json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; - auto p2 = j2.template get(); + auto p2 = j2.get(); // incomplete deserialization: json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json; - auto p3 = j3.template get(); + auto p3 = j3.get(); std::cout << "roundtrip: " << json(p3) << std::endl; } diff --git a/docs/examples/nlohmann_define_type_non_intrusive_with_default_macro.output b/docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_default_macro.output similarity index 100% rename from docs/examples/nlohmann_define_type_non_intrusive_with_default_macro.output rename to docs/mkdocs/docs/examples/nlohmann_define_type_non_intrusive_with_default_macro.output diff --git a/docs/examples/nlohmann_json_namespace.cpp b/docs/mkdocs/docs/examples/nlohmann_json_namespace.cpp similarity index 100% rename from docs/examples/nlohmann_json_namespace.cpp rename to docs/mkdocs/docs/examples/nlohmann_json_namespace.cpp diff --git a/docs/mkdocs/docs/examples/nlohmann_json_namespace.output b/docs/mkdocs/docs/examples/nlohmann_json_namespace.output new file mode 100644 index 0000000000..e0cb53b104 --- /dev/null +++ b/docs/mkdocs/docs/examples/nlohmann_json_namespace.output @@ -0,0 +1 @@ +nlohmann::json_abi_v3_12_0 diff --git a/docs/examples/nlohmann_json_namespace_begin.c++17.cpp b/docs/mkdocs/docs/examples/nlohmann_json_namespace_begin.c++17.cpp similarity index 100% rename from docs/examples/nlohmann_json_namespace_begin.c++17.cpp rename to docs/mkdocs/docs/examples/nlohmann_json_namespace_begin.c++17.cpp diff --git a/docs/examples/nlohmann_json_namespace_begin.c++17.output b/docs/mkdocs/docs/examples/nlohmann_json_namespace_begin.c++17.output similarity index 100% rename from docs/examples/nlohmann_json_namespace_begin.c++17.output rename to docs/mkdocs/docs/examples/nlohmann_json_namespace_begin.c++17.output diff --git a/docs/examples/nlohmann_json_namespace_no_version.cpp b/docs/mkdocs/docs/examples/nlohmann_json_namespace_no_version.cpp similarity index 100% rename from docs/examples/nlohmann_json_namespace_no_version.cpp rename to docs/mkdocs/docs/examples/nlohmann_json_namespace_no_version.cpp diff --git a/docs/examples/nlohmann_json_namespace_no_version.output b/docs/mkdocs/docs/examples/nlohmann_json_namespace_no_version.output similarity index 100% rename from docs/examples/nlohmann_json_namespace_no_version.output rename to docs/mkdocs/docs/examples/nlohmann_json_namespace_no_version.output diff --git a/docs/examples/nlohmann_json_serialize_enum.cpp b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum.cpp similarity index 85% rename from docs/examples/nlohmann_json_serialize_enum.cpp rename to docs/mkdocs/docs/examples/nlohmann_json_serialize_enum.cpp index a40db49eaa..f9e472c64b 100644 --- a/docs/examples/nlohmann_json_serialize_enum.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum.cpp @@ -44,16 +44,16 @@ int main() // deserialization json j_running = "running"; json j_blue = "blue"; - auto running = j_running.template get(); - auto blue = j_blue.template get(); + auto running = j_running.get(); + auto blue = j_blue.get(); std::cout << j_running << " -> " << running << ", " << j_blue << " -> " << static_cast(blue) << std::endl; // deserializing undefined JSON value to enum // (where the first map entry above is the default) json j_pi = 3.14; - auto invalid = j_pi.template get(); - auto unknown = j_pi.template get(); + auto invalid = j_pi.get(); + auto unknown = j_pi.get(); std::cout << j_pi << " -> " << invalid << ", " << j_pi << " -> " << static_cast(unknown) << std::endl; } diff --git a/docs/examples/nlohmann_json_serialize_enum.output b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum.output similarity index 100% rename from docs/examples/nlohmann_json_serialize_enum.output rename to docs/mkdocs/docs/examples/nlohmann_json_serialize_enum.output diff --git a/docs/examples/nlohmann_json_serialize_enum_2.cpp b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_2.cpp similarity index 88% rename from docs/examples/nlohmann_json_serialize_enum_2.cpp rename to docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_2.cpp index b35e94cc56..fd27226ca9 100644 --- a/docs/examples/nlohmann_json_serialize_enum_2.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_2.cpp @@ -26,8 +26,8 @@ int main() // deserialization json j_rot = "rot"; - auto rot = j_rot.template get(); - auto red = j_red.template get(); + auto rot = j_rot.get(); + auto red = j_red.get(); std::cout << j_rot << " -> " << static_cast(rot) << std::endl; std::cout << j_red << " -> " << static_cast(red) << std::endl; } diff --git a/docs/examples/nlohmann_json_serialize_enum_2.output b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_2.output similarity index 100% rename from docs/examples/nlohmann_json_serialize_enum_2.output rename to docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_2.output diff --git a/docs/examples/nlohmann_json_version.cpp b/docs/mkdocs/docs/examples/nlohmann_json_version.cpp similarity index 100% rename from docs/examples/nlohmann_json_version.cpp rename to docs/mkdocs/docs/examples/nlohmann_json_version.cpp diff --git a/docs/mkdocs/docs/examples/nlohmann_json_version.output b/docs/mkdocs/docs/examples/nlohmann_json_version.output new file mode 100644 index 0000000000..0976940fcf --- /dev/null +++ b/docs/mkdocs/docs/examples/nlohmann_json_version.output @@ -0,0 +1 @@ +JSON for Modern C++ version 3.12.0 diff --git a/docs/examples/number_float_t.cpp b/docs/mkdocs/docs/examples/number_float_t.cpp similarity index 100% rename from docs/examples/number_float_t.cpp rename to docs/mkdocs/docs/examples/number_float_t.cpp diff --git a/docs/examples/number_float_t.output b/docs/mkdocs/docs/examples/number_float_t.output similarity index 100% rename from docs/examples/number_float_t.output rename to docs/mkdocs/docs/examples/number_float_t.output diff --git a/docs/examples/number_integer_t.cpp b/docs/mkdocs/docs/examples/number_integer_t.cpp similarity index 100% rename from docs/examples/number_integer_t.cpp rename to docs/mkdocs/docs/examples/number_integer_t.cpp diff --git a/docs/examples/number_integer_t.output b/docs/mkdocs/docs/examples/number_integer_t.output similarity index 100% rename from docs/examples/number_integer_t.output rename to docs/mkdocs/docs/examples/number_integer_t.output diff --git a/docs/examples/number_unsigned_t.cpp b/docs/mkdocs/docs/examples/number_unsigned_t.cpp similarity index 100% rename from docs/examples/number_unsigned_t.cpp rename to docs/mkdocs/docs/examples/number_unsigned_t.cpp diff --git a/docs/examples/number_unsigned_t.output b/docs/mkdocs/docs/examples/number_unsigned_t.output similarity index 100% rename from docs/examples/number_unsigned_t.output rename to docs/mkdocs/docs/examples/number_unsigned_t.output diff --git a/docs/examples/object.cpp b/docs/mkdocs/docs/examples/object.cpp similarity index 100% rename from docs/examples/object.cpp rename to docs/mkdocs/docs/examples/object.cpp diff --git a/docs/examples/object.output b/docs/mkdocs/docs/examples/object.output similarity index 100% rename from docs/examples/object.output rename to docs/mkdocs/docs/examples/object.output diff --git a/docs/examples/object_comparator_t.cpp b/docs/mkdocs/docs/examples/object_comparator_t.cpp similarity index 100% rename from docs/examples/object_comparator_t.cpp rename to docs/mkdocs/docs/examples/object_comparator_t.cpp diff --git a/docs/examples/object_comparator_t.output b/docs/mkdocs/docs/examples/object_comparator_t.output similarity index 100% rename from docs/examples/object_comparator_t.output rename to docs/mkdocs/docs/examples/object_comparator_t.output diff --git a/docs/examples/object_t.cpp b/docs/mkdocs/docs/examples/object_t.cpp similarity index 100% rename from docs/examples/object_t.cpp rename to docs/mkdocs/docs/examples/object_t.cpp diff --git a/docs/examples/object_t.output b/docs/mkdocs/docs/examples/object_t.output similarity index 100% rename from docs/examples/object_t.output rename to docs/mkdocs/docs/examples/object_t.output diff --git a/docs/examples/operator__ValueType.cpp b/docs/mkdocs/docs/examples/operator__ValueType.cpp similarity index 100% rename from docs/examples/operator__ValueType.cpp rename to docs/mkdocs/docs/examples/operator__ValueType.cpp diff --git a/docs/examples/operator__ValueType.output b/docs/mkdocs/docs/examples/operator__ValueType.output similarity index 100% rename from docs/examples/operator__ValueType.output rename to docs/mkdocs/docs/examples/operator__ValueType.output diff --git a/docs/examples/operator__equal.cpp b/docs/mkdocs/docs/examples/operator__equal.cpp similarity index 100% rename from docs/examples/operator__equal.cpp rename to docs/mkdocs/docs/examples/operator__equal.cpp diff --git a/docs/examples/operator__equal.output b/docs/mkdocs/docs/examples/operator__equal.output similarity index 100% rename from docs/examples/operator__equal.output rename to docs/mkdocs/docs/examples/operator__equal.output diff --git a/docs/examples/operator__equal__nullptr_t.cpp b/docs/mkdocs/docs/examples/operator__equal__nullptr_t.cpp similarity index 100% rename from docs/examples/operator__equal__nullptr_t.cpp rename to docs/mkdocs/docs/examples/operator__equal__nullptr_t.cpp diff --git a/docs/examples/operator__equal__nullptr_t.output b/docs/mkdocs/docs/examples/operator__equal__nullptr_t.output similarity index 100% rename from docs/examples/operator__equal__nullptr_t.output rename to docs/mkdocs/docs/examples/operator__equal__nullptr_t.output diff --git a/docs/examples/operator__equal__specializations.cpp b/docs/mkdocs/docs/examples/operator__equal__specializations.cpp similarity index 100% rename from docs/examples/operator__equal__specializations.cpp rename to docs/mkdocs/docs/examples/operator__equal__specializations.cpp diff --git a/docs/examples/operator__equal__specializations.output b/docs/mkdocs/docs/examples/operator__equal__specializations.output similarity index 100% rename from docs/examples/operator__equal__specializations.output rename to docs/mkdocs/docs/examples/operator__equal__specializations.output diff --git a/docs/examples/operator__greater.cpp b/docs/mkdocs/docs/examples/operator__greater.cpp similarity index 100% rename from docs/examples/operator__greater.cpp rename to docs/mkdocs/docs/examples/operator__greater.cpp diff --git a/docs/examples/operator__greater.output b/docs/mkdocs/docs/examples/operator__greater.output similarity index 100% rename from docs/examples/operator__greater.output rename to docs/mkdocs/docs/examples/operator__greater.output diff --git a/docs/examples/operator__greaterequal.cpp b/docs/mkdocs/docs/examples/operator__greaterequal.cpp similarity index 100% rename from docs/examples/operator__greaterequal.cpp rename to docs/mkdocs/docs/examples/operator__greaterequal.cpp diff --git a/docs/examples/operator__greaterequal.output b/docs/mkdocs/docs/examples/operator__greaterequal.output similarity index 100% rename from docs/examples/operator__greaterequal.output rename to docs/mkdocs/docs/examples/operator__greaterequal.output diff --git a/docs/examples/operator__less.cpp b/docs/mkdocs/docs/examples/operator__less.cpp similarity index 100% rename from docs/examples/operator__less.cpp rename to docs/mkdocs/docs/examples/operator__less.cpp diff --git a/docs/examples/operator__less.output b/docs/mkdocs/docs/examples/operator__less.output similarity index 100% rename from docs/examples/operator__less.output rename to docs/mkdocs/docs/examples/operator__less.output diff --git a/docs/examples/operator__lessequal.cpp b/docs/mkdocs/docs/examples/operator__lessequal.cpp similarity index 100% rename from docs/examples/operator__lessequal.cpp rename to docs/mkdocs/docs/examples/operator__lessequal.cpp diff --git a/docs/examples/operator__lessequal.output b/docs/mkdocs/docs/examples/operator__lessequal.output similarity index 100% rename from docs/examples/operator__lessequal.output rename to docs/mkdocs/docs/examples/operator__lessequal.output diff --git a/docs/examples/operator__notequal.cpp b/docs/mkdocs/docs/examples/operator__notequal.cpp similarity index 100% rename from docs/examples/operator__notequal.cpp rename to docs/mkdocs/docs/examples/operator__notequal.cpp diff --git a/docs/examples/operator__notequal.output b/docs/mkdocs/docs/examples/operator__notequal.output similarity index 100% rename from docs/examples/operator__notequal.output rename to docs/mkdocs/docs/examples/operator__notequal.output diff --git a/docs/examples/operator__notequal__nullptr_t.cpp b/docs/mkdocs/docs/examples/operator__notequal__nullptr_t.cpp similarity index 100% rename from docs/examples/operator__notequal__nullptr_t.cpp rename to docs/mkdocs/docs/examples/operator__notequal__nullptr_t.cpp diff --git a/docs/examples/operator__notequal__nullptr_t.output b/docs/mkdocs/docs/examples/operator__notequal__nullptr_t.output similarity index 100% rename from docs/examples/operator__notequal__nullptr_t.output rename to docs/mkdocs/docs/examples/operator__notequal__nullptr_t.output diff --git a/docs/examples/operator__value_t.cpp b/docs/mkdocs/docs/examples/operator__value_t.cpp similarity index 100% rename from docs/examples/operator__value_t.cpp rename to docs/mkdocs/docs/examples/operator__value_t.cpp diff --git a/docs/examples/operator__value_t.output b/docs/mkdocs/docs/examples/operator__value_t.output similarity index 100% rename from docs/examples/operator__value_t.output rename to docs/mkdocs/docs/examples/operator__value_t.output diff --git a/docs/examples/operator_array__json_pointer.cpp b/docs/mkdocs/docs/examples/operator_array__json_pointer.cpp similarity index 100% rename from docs/examples/operator_array__json_pointer.cpp rename to docs/mkdocs/docs/examples/operator_array__json_pointer.cpp diff --git a/docs/examples/operator_array__json_pointer.output b/docs/mkdocs/docs/examples/operator_array__json_pointer.output similarity index 100% rename from docs/examples/operator_array__json_pointer.output rename to docs/mkdocs/docs/examples/operator_array__json_pointer.output diff --git a/docs/examples/operator_array__json_pointer_const.cpp b/docs/mkdocs/docs/examples/operator_array__json_pointer_const.cpp similarity index 100% rename from docs/examples/operator_array__json_pointer_const.cpp rename to docs/mkdocs/docs/examples/operator_array__json_pointer_const.cpp diff --git a/docs/examples/operator_array__json_pointer_const.output b/docs/mkdocs/docs/examples/operator_array__json_pointer_const.output similarity index 100% rename from docs/examples/operator_array__json_pointer_const.output rename to docs/mkdocs/docs/examples/operator_array__json_pointer_const.output diff --git a/docs/examples/operator_array__keytype.c++17.cpp b/docs/mkdocs/docs/examples/operator_array__keytype.c++17.cpp similarity index 100% rename from docs/examples/operator_array__keytype.c++17.cpp rename to docs/mkdocs/docs/examples/operator_array__keytype.c++17.cpp diff --git a/docs/examples/operator_array__keytype.c++17.output b/docs/mkdocs/docs/examples/operator_array__keytype.c++17.output similarity index 100% rename from docs/examples/operator_array__keytype.c++17.output rename to docs/mkdocs/docs/examples/operator_array__keytype.c++17.output diff --git a/docs/examples/operator_array__keytype_const.c++17.cpp b/docs/mkdocs/docs/examples/operator_array__keytype_const.c++17.cpp similarity index 100% rename from docs/examples/operator_array__keytype_const.c++17.cpp rename to docs/mkdocs/docs/examples/operator_array__keytype_const.c++17.cpp diff --git a/docs/examples/operator_array__keytype_const.c++17.output b/docs/mkdocs/docs/examples/operator_array__keytype_const.c++17.output similarity index 100% rename from docs/examples/operator_array__keytype_const.c++17.output rename to docs/mkdocs/docs/examples/operator_array__keytype_const.c++17.output diff --git a/docs/examples/operator_array__object_t_key_type.cpp b/docs/mkdocs/docs/examples/operator_array__object_t_key_type.cpp similarity index 100% rename from docs/examples/operator_array__object_t_key_type.cpp rename to docs/mkdocs/docs/examples/operator_array__object_t_key_type.cpp diff --git a/docs/examples/operator_array__object_t_key_type.output b/docs/mkdocs/docs/examples/operator_array__object_t_key_type.output similarity index 100% rename from docs/examples/operator_array__object_t_key_type.output rename to docs/mkdocs/docs/examples/operator_array__object_t_key_type.output diff --git a/docs/examples/operator_array__object_t_key_type_const.cpp b/docs/mkdocs/docs/examples/operator_array__object_t_key_type_const.cpp similarity index 100% rename from docs/examples/operator_array__object_t_key_type_const.cpp rename to docs/mkdocs/docs/examples/operator_array__object_t_key_type_const.cpp diff --git a/docs/examples/operator_array__object_t_key_type_const.output b/docs/mkdocs/docs/examples/operator_array__object_t_key_type_const.output similarity index 100% rename from docs/examples/operator_array__object_t_key_type_const.output rename to docs/mkdocs/docs/examples/operator_array__object_t_key_type_const.output diff --git a/docs/examples/operator_array__size_type.cpp b/docs/mkdocs/docs/examples/operator_array__size_type.cpp similarity index 100% rename from docs/examples/operator_array__size_type.cpp rename to docs/mkdocs/docs/examples/operator_array__size_type.cpp diff --git a/docs/examples/operator_array__size_type.output b/docs/mkdocs/docs/examples/operator_array__size_type.output similarity index 100% rename from docs/examples/operator_array__size_type.output rename to docs/mkdocs/docs/examples/operator_array__size_type.output diff --git a/docs/examples/operator_array__size_type_const.cpp b/docs/mkdocs/docs/examples/operator_array__size_type_const.cpp similarity index 100% rename from docs/examples/operator_array__size_type_const.cpp rename to docs/mkdocs/docs/examples/operator_array__size_type_const.cpp diff --git a/docs/examples/operator_array__size_type_const.output b/docs/mkdocs/docs/examples/operator_array__size_type_const.output similarity index 100% rename from docs/examples/operator_array__size_type_const.output rename to docs/mkdocs/docs/examples/operator_array__size_type_const.output diff --git a/docs/examples/operator_deserialize.cpp b/docs/mkdocs/docs/examples/operator_deserialize.cpp similarity index 100% rename from docs/examples/operator_deserialize.cpp rename to docs/mkdocs/docs/examples/operator_deserialize.cpp diff --git a/docs/examples/operator_deserialize.output b/docs/mkdocs/docs/examples/operator_deserialize.output similarity index 100% rename from docs/examples/operator_deserialize.output rename to docs/mkdocs/docs/examples/operator_deserialize.output diff --git a/docs/examples/operator_literal_json.cpp b/docs/mkdocs/docs/examples/operator_literal_json.cpp similarity index 100% rename from docs/examples/operator_literal_json.cpp rename to docs/mkdocs/docs/examples/operator_literal_json.cpp diff --git a/docs/examples/operator_literal_json.output b/docs/mkdocs/docs/examples/operator_literal_json.output similarity index 100% rename from docs/examples/operator_literal_json.output rename to docs/mkdocs/docs/examples/operator_literal_json.output diff --git a/docs/examples/operator_literal_json_pointer.cpp b/docs/mkdocs/docs/examples/operator_literal_json_pointer.cpp similarity index 100% rename from docs/examples/operator_literal_json_pointer.cpp rename to docs/mkdocs/docs/examples/operator_literal_json_pointer.cpp diff --git a/docs/examples/operator_literal_json_pointer.output b/docs/mkdocs/docs/examples/operator_literal_json_pointer.output similarity index 100% rename from docs/examples/operator_literal_json_pointer.output rename to docs/mkdocs/docs/examples/operator_literal_json_pointer.output diff --git a/docs/examples/operator_ltlt__basic_json.cpp b/docs/mkdocs/docs/examples/operator_ltlt__basic_json.cpp similarity index 100% rename from docs/examples/operator_ltlt__basic_json.cpp rename to docs/mkdocs/docs/examples/operator_ltlt__basic_json.cpp diff --git a/docs/examples/operator_ltlt__basic_json.output b/docs/mkdocs/docs/examples/operator_ltlt__basic_json.output similarity index 100% rename from docs/examples/operator_ltlt__basic_json.output rename to docs/mkdocs/docs/examples/operator_ltlt__basic_json.output diff --git a/docs/examples/operator_ltlt__json_pointer.cpp b/docs/mkdocs/docs/examples/operator_ltlt__json_pointer.cpp similarity index 89% rename from docs/examples/operator_ltlt__json_pointer.cpp rename to docs/mkdocs/docs/examples/operator_ltlt__json_pointer.cpp index f4fac886d6..044318fa56 100644 --- a/docs/examples/operator_ltlt__json_pointer.cpp +++ b/docs/mkdocs/docs/examples/operator_ltlt__json_pointer.cpp @@ -5,7 +5,7 @@ using json = nlohmann::json; int main() { - // create JSON poiner + // create JSON pointer json::json_pointer ptr("/foo/bar/baz"); // write string representation to stream diff --git a/docs/examples/operator_ltlt__json_pointer.output b/docs/mkdocs/docs/examples/operator_ltlt__json_pointer.output similarity index 100% rename from docs/examples/operator_ltlt__json_pointer.output rename to docs/mkdocs/docs/examples/operator_ltlt__json_pointer.output diff --git a/docs/examples/operator_spaceship__const_reference.c++20.cpp b/docs/mkdocs/docs/examples/operator_spaceship__const_reference.c++20.cpp similarity index 100% rename from docs/examples/operator_spaceship__const_reference.c++20.cpp rename to docs/mkdocs/docs/examples/operator_spaceship__const_reference.c++20.cpp diff --git a/docs/examples/operator_spaceship__const_reference.c++20.output b/docs/mkdocs/docs/examples/operator_spaceship__const_reference.c++20.output similarity index 100% rename from docs/examples/operator_spaceship__const_reference.c++20.output rename to docs/mkdocs/docs/examples/operator_spaceship__const_reference.c++20.output diff --git a/docs/examples/operator_spaceship__scalartype.c++20.cpp b/docs/mkdocs/docs/examples/operator_spaceship__scalartype.c++20.cpp similarity index 100% rename from docs/examples/operator_spaceship__scalartype.c++20.cpp rename to docs/mkdocs/docs/examples/operator_spaceship__scalartype.c++20.cpp diff --git a/docs/examples/operator_spaceship__scalartype.c++20.output b/docs/mkdocs/docs/examples/operator_spaceship__scalartype.c++20.output similarity index 100% rename from docs/examples/operator_spaceship__scalartype.c++20.output rename to docs/mkdocs/docs/examples/operator_spaceship__scalartype.c++20.output diff --git a/docs/examples/ordered_json.cpp b/docs/mkdocs/docs/examples/ordered_json.cpp similarity index 100% rename from docs/examples/ordered_json.cpp rename to docs/mkdocs/docs/examples/ordered_json.cpp diff --git a/docs/examples/ordered_json.output b/docs/mkdocs/docs/examples/ordered_json.output similarity index 100% rename from docs/examples/ordered_json.output rename to docs/mkdocs/docs/examples/ordered_json.output diff --git a/docs/examples/ordered_map.cpp b/docs/mkdocs/docs/examples/ordered_map.cpp similarity index 100% rename from docs/examples/ordered_map.cpp rename to docs/mkdocs/docs/examples/ordered_map.cpp diff --git a/docs/examples/ordered_map.output b/docs/mkdocs/docs/examples/ordered_map.output similarity index 100% rename from docs/examples/ordered_map.output rename to docs/mkdocs/docs/examples/ordered_map.output diff --git a/docs/examples/other_error.cpp b/docs/mkdocs/docs/examples/other_error.cpp similarity index 100% rename from docs/examples/other_error.cpp rename to docs/mkdocs/docs/examples/other_error.cpp diff --git a/docs/examples/other_error.output b/docs/mkdocs/docs/examples/other_error.output similarity index 100% rename from docs/examples/other_error.output rename to docs/mkdocs/docs/examples/other_error.output diff --git a/docs/examples/out_of_range.cpp b/docs/mkdocs/docs/examples/out_of_range.cpp similarity index 100% rename from docs/examples/out_of_range.cpp rename to docs/mkdocs/docs/examples/out_of_range.cpp diff --git a/docs/examples/out_of_range.output b/docs/mkdocs/docs/examples/out_of_range.output similarity index 100% rename from docs/examples/out_of_range.output rename to docs/mkdocs/docs/examples/out_of_range.output diff --git a/docs/examples/parse__allow_exceptions.cpp b/docs/mkdocs/docs/examples/parse__allow_exceptions.cpp similarity index 100% rename from docs/examples/parse__allow_exceptions.cpp rename to docs/mkdocs/docs/examples/parse__allow_exceptions.cpp diff --git a/docs/examples/parse__allow_exceptions.output b/docs/mkdocs/docs/examples/parse__allow_exceptions.output similarity index 100% rename from docs/examples/parse__allow_exceptions.output rename to docs/mkdocs/docs/examples/parse__allow_exceptions.output diff --git a/docs/examples/parse__array__parser_callback_t.cpp b/docs/mkdocs/docs/examples/parse__array__parser_callback_t.cpp similarity index 100% rename from docs/examples/parse__array__parser_callback_t.cpp rename to docs/mkdocs/docs/examples/parse__array__parser_callback_t.cpp diff --git a/docs/examples/parse__array__parser_callback_t.output b/docs/mkdocs/docs/examples/parse__array__parser_callback_t.output similarity index 100% rename from docs/examples/parse__array__parser_callback_t.output rename to docs/mkdocs/docs/examples/parse__array__parser_callback_t.output diff --git a/docs/examples/parse__contiguouscontainer__parser_callback_t.cpp b/docs/mkdocs/docs/examples/parse__contiguouscontainer__parser_callback_t.cpp similarity index 100% rename from docs/examples/parse__contiguouscontainer__parser_callback_t.cpp rename to docs/mkdocs/docs/examples/parse__contiguouscontainer__parser_callback_t.cpp diff --git a/docs/examples/parse__contiguouscontainer__parser_callback_t.output b/docs/mkdocs/docs/examples/parse__contiguouscontainer__parser_callback_t.output similarity index 100% rename from docs/examples/parse__contiguouscontainer__parser_callback_t.output rename to docs/mkdocs/docs/examples/parse__contiguouscontainer__parser_callback_t.output diff --git a/docs/examples/parse__istream__parser_callback_t.cpp b/docs/mkdocs/docs/examples/parse__istream__parser_callback_t.cpp similarity index 100% rename from docs/examples/parse__istream__parser_callback_t.cpp rename to docs/mkdocs/docs/examples/parse__istream__parser_callback_t.cpp diff --git a/docs/examples/parse__istream__parser_callback_t.output b/docs/mkdocs/docs/examples/parse__istream__parser_callback_t.output similarity index 100% rename from docs/examples/parse__istream__parser_callback_t.output rename to docs/mkdocs/docs/examples/parse__istream__parser_callback_t.output diff --git a/docs/examples/parse__iterator_pair.cpp b/docs/mkdocs/docs/examples/parse__iterator_pair.cpp similarity index 100% rename from docs/examples/parse__iterator_pair.cpp rename to docs/mkdocs/docs/examples/parse__iterator_pair.cpp diff --git a/docs/examples/parse__iterator_pair.link b/docs/mkdocs/docs/examples/parse__iterator_pair.link similarity index 100% rename from docs/examples/parse__iterator_pair.link rename to docs/mkdocs/docs/examples/parse__iterator_pair.link diff --git a/docs/examples/parse__iterator_pair.output b/docs/mkdocs/docs/examples/parse__iterator_pair.output similarity index 100% rename from docs/examples/parse__iterator_pair.output rename to docs/mkdocs/docs/examples/parse__iterator_pair.output diff --git a/docs/examples/parse__pointers.cpp b/docs/mkdocs/docs/examples/parse__pointers.cpp similarity index 100% rename from docs/examples/parse__pointers.cpp rename to docs/mkdocs/docs/examples/parse__pointers.cpp diff --git a/docs/examples/parse__pointers.link b/docs/mkdocs/docs/examples/parse__pointers.link similarity index 100% rename from docs/examples/parse__pointers.link rename to docs/mkdocs/docs/examples/parse__pointers.link diff --git a/docs/examples/parse__pointers.output b/docs/mkdocs/docs/examples/parse__pointers.output similarity index 100% rename from docs/examples/parse__pointers.output rename to docs/mkdocs/docs/examples/parse__pointers.output diff --git a/docs/examples/parse__string__parser_callback_t.cpp b/docs/mkdocs/docs/examples/parse__string__parser_callback_t.cpp similarity index 100% rename from docs/examples/parse__string__parser_callback_t.cpp rename to docs/mkdocs/docs/examples/parse__string__parser_callback_t.cpp diff --git a/docs/examples/parse__string__parser_callback_t.output b/docs/mkdocs/docs/examples/parse__string__parser_callback_t.output similarity index 100% rename from docs/examples/parse__string__parser_callback_t.output rename to docs/mkdocs/docs/examples/parse__string__parser_callback_t.output diff --git a/docs/examples/parse_error.cpp b/docs/mkdocs/docs/examples/parse_error.cpp similarity index 100% rename from docs/examples/parse_error.cpp rename to docs/mkdocs/docs/examples/parse_error.cpp diff --git a/docs/examples/parse_error.output b/docs/mkdocs/docs/examples/parse_error.output similarity index 100% rename from docs/examples/parse_error.output rename to docs/mkdocs/docs/examples/parse_error.output diff --git a/docs/examples/patch.cpp b/docs/mkdocs/docs/examples/patch.cpp similarity index 100% rename from docs/examples/patch.cpp rename to docs/mkdocs/docs/examples/patch.cpp diff --git a/docs/examples/patch.output b/docs/mkdocs/docs/examples/patch.output similarity index 100% rename from docs/examples/patch.output rename to docs/mkdocs/docs/examples/patch.output diff --git a/docs/examples/patch_inplace.cpp b/docs/mkdocs/docs/examples/patch_inplace.cpp similarity index 100% rename from docs/examples/patch_inplace.cpp rename to docs/mkdocs/docs/examples/patch_inplace.cpp diff --git a/docs/examples/patch_inplace.output b/docs/mkdocs/docs/examples/patch_inplace.output similarity index 100% rename from docs/examples/patch_inplace.output rename to docs/mkdocs/docs/examples/patch_inplace.output diff --git a/docs/examples/push_back.cpp b/docs/mkdocs/docs/examples/push_back.cpp similarity index 100% rename from docs/examples/push_back.cpp rename to docs/mkdocs/docs/examples/push_back.cpp diff --git a/docs/examples/push_back.output b/docs/mkdocs/docs/examples/push_back.output similarity index 100% rename from docs/examples/push_back.output rename to docs/mkdocs/docs/examples/push_back.output diff --git a/docs/examples/push_back__initializer_list.cpp b/docs/mkdocs/docs/examples/push_back__initializer_list.cpp similarity index 100% rename from docs/examples/push_back__initializer_list.cpp rename to docs/mkdocs/docs/examples/push_back__initializer_list.cpp diff --git a/docs/examples/push_back__initializer_list.output b/docs/mkdocs/docs/examples/push_back__initializer_list.output similarity index 100% rename from docs/examples/push_back__initializer_list.output rename to docs/mkdocs/docs/examples/push_back__initializer_list.output diff --git a/docs/examples/push_back__object_t__value.cpp b/docs/mkdocs/docs/examples/push_back__object_t__value.cpp similarity index 100% rename from docs/examples/push_back__object_t__value.cpp rename to docs/mkdocs/docs/examples/push_back__object_t__value.cpp diff --git a/docs/examples/push_back__object_t__value.output b/docs/mkdocs/docs/examples/push_back__object_t__value.output similarity index 100% rename from docs/examples/push_back__object_t__value.output rename to docs/mkdocs/docs/examples/push_back__object_t__value.output diff --git a/docs/examples/rbegin.cpp b/docs/mkdocs/docs/examples/rbegin.cpp similarity index 100% rename from docs/examples/rbegin.cpp rename to docs/mkdocs/docs/examples/rbegin.cpp diff --git a/docs/examples/rbegin.output b/docs/mkdocs/docs/examples/rbegin.output similarity index 100% rename from docs/examples/rbegin.output rename to docs/mkdocs/docs/examples/rbegin.output diff --git a/docs/examples/rend.cpp b/docs/mkdocs/docs/examples/rend.cpp similarity index 100% rename from docs/examples/rend.cpp rename to docs/mkdocs/docs/examples/rend.cpp diff --git a/docs/examples/rend.output b/docs/mkdocs/docs/examples/rend.output similarity index 100% rename from docs/examples/rend.output rename to docs/mkdocs/docs/examples/rend.output diff --git a/docs/examples/sax_parse.cpp b/docs/mkdocs/docs/examples/sax_parse.cpp similarity index 100% rename from docs/examples/sax_parse.cpp rename to docs/mkdocs/docs/examples/sax_parse.cpp diff --git a/docs/examples/sax_parse.output b/docs/mkdocs/docs/examples/sax_parse.output similarity index 100% rename from docs/examples/sax_parse.output rename to docs/mkdocs/docs/examples/sax_parse.output diff --git a/docs/examples/sax_parse__binary.cpp b/docs/mkdocs/docs/examples/sax_parse__binary.cpp similarity index 100% rename from docs/examples/sax_parse__binary.cpp rename to docs/mkdocs/docs/examples/sax_parse__binary.cpp diff --git a/docs/examples/sax_parse__binary.output b/docs/mkdocs/docs/examples/sax_parse__binary.output similarity index 100% rename from docs/examples/sax_parse__binary.output rename to docs/mkdocs/docs/examples/sax_parse__binary.output diff --git a/docs/examples/size.cpp b/docs/mkdocs/docs/examples/size.cpp similarity index 100% rename from docs/examples/size.cpp rename to docs/mkdocs/docs/examples/size.cpp diff --git a/docs/examples/size.output b/docs/mkdocs/docs/examples/size.output similarity index 100% rename from docs/examples/size.output rename to docs/mkdocs/docs/examples/size.output diff --git a/docs/examples/std_hash.cpp b/docs/mkdocs/docs/examples/std_hash.cpp similarity index 100% rename from docs/examples/std_hash.cpp rename to docs/mkdocs/docs/examples/std_hash.cpp diff --git a/docs/examples/std_hash.output b/docs/mkdocs/docs/examples/std_hash.output similarity index 100% rename from docs/examples/std_hash.output rename to docs/mkdocs/docs/examples/std_hash.output diff --git a/docs/examples/std_swap.cpp b/docs/mkdocs/docs/examples/std_swap.cpp similarity index 100% rename from docs/examples/std_swap.cpp rename to docs/mkdocs/docs/examples/std_swap.cpp diff --git a/docs/examples/std_swap.output b/docs/mkdocs/docs/examples/std_swap.output similarity index 100% rename from docs/examples/std_swap.output rename to docs/mkdocs/docs/examples/std_swap.output diff --git a/docs/examples/string_t.cpp b/docs/mkdocs/docs/examples/string_t.cpp similarity index 100% rename from docs/examples/string_t.cpp rename to docs/mkdocs/docs/examples/string_t.cpp diff --git a/docs/examples/string_t.output b/docs/mkdocs/docs/examples/string_t.output similarity index 100% rename from docs/examples/string_t.output rename to docs/mkdocs/docs/examples/string_t.output diff --git a/docs/examples/swap__array_t.cpp b/docs/mkdocs/docs/examples/swap__array_t.cpp similarity index 100% rename from docs/examples/swap__array_t.cpp rename to docs/mkdocs/docs/examples/swap__array_t.cpp diff --git a/docs/examples/swap__array_t.output b/docs/mkdocs/docs/examples/swap__array_t.output similarity index 100% rename from docs/examples/swap__array_t.output rename to docs/mkdocs/docs/examples/swap__array_t.output diff --git a/docs/examples/swap__binary_t.cpp b/docs/mkdocs/docs/examples/swap__binary_t.cpp similarity index 100% rename from docs/examples/swap__binary_t.cpp rename to docs/mkdocs/docs/examples/swap__binary_t.cpp diff --git a/docs/examples/swap__binary_t.output b/docs/mkdocs/docs/examples/swap__binary_t.output similarity index 100% rename from docs/examples/swap__binary_t.output rename to docs/mkdocs/docs/examples/swap__binary_t.output diff --git a/docs/examples/swap__object_t.cpp b/docs/mkdocs/docs/examples/swap__object_t.cpp similarity index 100% rename from docs/examples/swap__object_t.cpp rename to docs/mkdocs/docs/examples/swap__object_t.cpp diff --git a/docs/examples/swap__object_t.output b/docs/mkdocs/docs/examples/swap__object_t.output similarity index 100% rename from docs/examples/swap__object_t.output rename to docs/mkdocs/docs/examples/swap__object_t.output diff --git a/docs/examples/swap__reference.cpp b/docs/mkdocs/docs/examples/swap__reference.cpp similarity index 100% rename from docs/examples/swap__reference.cpp rename to docs/mkdocs/docs/examples/swap__reference.cpp diff --git a/docs/examples/swap__reference.output b/docs/mkdocs/docs/examples/swap__reference.output similarity index 100% rename from docs/examples/swap__reference.output rename to docs/mkdocs/docs/examples/swap__reference.output diff --git a/docs/examples/swap__string_t.cpp b/docs/mkdocs/docs/examples/swap__string_t.cpp similarity index 100% rename from docs/examples/swap__string_t.cpp rename to docs/mkdocs/docs/examples/swap__string_t.cpp diff --git a/docs/examples/swap__string_t.output b/docs/mkdocs/docs/examples/swap__string_t.output similarity index 100% rename from docs/examples/swap__string_t.output rename to docs/mkdocs/docs/examples/swap__string_t.output diff --git a/docs/examples/to_bjdata.cpp b/docs/mkdocs/docs/examples/to_bjdata.cpp similarity index 100% rename from docs/examples/to_bjdata.cpp rename to docs/mkdocs/docs/examples/to_bjdata.cpp diff --git a/docs/examples/to_bjdata.output b/docs/mkdocs/docs/examples/to_bjdata.output similarity index 100% rename from docs/examples/to_bjdata.output rename to docs/mkdocs/docs/examples/to_bjdata.output diff --git a/docs/examples/to_bson.cpp b/docs/mkdocs/docs/examples/to_bson.cpp similarity index 100% rename from docs/examples/to_bson.cpp rename to docs/mkdocs/docs/examples/to_bson.cpp diff --git a/docs/examples/to_bson.output b/docs/mkdocs/docs/examples/to_bson.output similarity index 100% rename from docs/examples/to_bson.output rename to docs/mkdocs/docs/examples/to_bson.output diff --git a/docs/examples/to_cbor.cpp b/docs/mkdocs/docs/examples/to_cbor.cpp similarity index 100% rename from docs/examples/to_cbor.cpp rename to docs/mkdocs/docs/examples/to_cbor.cpp diff --git a/docs/examples/to_cbor.output b/docs/mkdocs/docs/examples/to_cbor.output similarity index 100% rename from docs/examples/to_cbor.output rename to docs/mkdocs/docs/examples/to_cbor.output diff --git a/docs/examples/to_json.cpp b/docs/mkdocs/docs/examples/to_json.cpp similarity index 100% rename from docs/examples/to_json.cpp rename to docs/mkdocs/docs/examples/to_json.cpp diff --git a/docs/examples/to_json.output b/docs/mkdocs/docs/examples/to_json.output similarity index 100% rename from docs/examples/to_json.output rename to docs/mkdocs/docs/examples/to_json.output diff --git a/docs/examples/to_msgpack.cpp b/docs/mkdocs/docs/examples/to_msgpack.cpp similarity index 100% rename from docs/examples/to_msgpack.cpp rename to docs/mkdocs/docs/examples/to_msgpack.cpp diff --git a/docs/examples/to_msgpack.output b/docs/mkdocs/docs/examples/to_msgpack.output similarity index 100% rename from docs/examples/to_msgpack.output rename to docs/mkdocs/docs/examples/to_msgpack.output diff --git a/docs/examples/to_string.cpp b/docs/mkdocs/docs/examples/to_string.cpp similarity index 100% rename from docs/examples/to_string.cpp rename to docs/mkdocs/docs/examples/to_string.cpp diff --git a/docs/examples/to_string.output b/docs/mkdocs/docs/examples/to_string.output similarity index 100% rename from docs/examples/to_string.output rename to docs/mkdocs/docs/examples/to_string.output diff --git a/docs/examples/to_ubjson.cpp b/docs/mkdocs/docs/examples/to_ubjson.cpp similarity index 100% rename from docs/examples/to_ubjson.cpp rename to docs/mkdocs/docs/examples/to_ubjson.cpp diff --git a/docs/examples/to_ubjson.output b/docs/mkdocs/docs/examples/to_ubjson.output similarity index 100% rename from docs/examples/to_ubjson.output rename to docs/mkdocs/docs/examples/to_ubjson.output diff --git a/docs/mkdocs/docs/examples/trailing_commas.cpp b/docs/mkdocs/docs/examples/trailing_commas.cpp new file mode 100644 index 0000000000..32bf6874eb --- /dev/null +++ b/docs/mkdocs/docs/examples/trailing_commas.cpp @@ -0,0 +1,37 @@ +#include +#include + +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'; +} diff --git a/docs/mkdocs/docs/examples/trailing_commas.output b/docs/mkdocs/docs/examples/trailing_commas.output new file mode 100644 index 0000000000..3b2c49eb77 --- /dev/null +++ b/docs/mkdocs/docs/examples/trailing_commas.output @@ -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" + ] +} diff --git a/docs/examples/type.cpp b/docs/mkdocs/docs/examples/type.cpp similarity index 100% rename from docs/examples/type.cpp rename to docs/mkdocs/docs/examples/type.cpp diff --git a/docs/examples/type.output b/docs/mkdocs/docs/examples/type.output similarity index 100% rename from docs/examples/type.output rename to docs/mkdocs/docs/examples/type.output diff --git a/docs/examples/type_error.cpp b/docs/mkdocs/docs/examples/type_error.cpp similarity index 100% rename from docs/examples/type_error.cpp rename to docs/mkdocs/docs/examples/type_error.cpp diff --git a/docs/examples/type_error.output b/docs/mkdocs/docs/examples/type_error.output similarity index 100% rename from docs/examples/type_error.output rename to docs/mkdocs/docs/examples/type_error.output diff --git a/docs/examples/type_name.cpp b/docs/mkdocs/docs/examples/type_name.cpp similarity index 100% rename from docs/examples/type_name.cpp rename to docs/mkdocs/docs/examples/type_name.cpp diff --git a/docs/examples/type_name.output b/docs/mkdocs/docs/examples/type_name.output similarity index 100% rename from docs/examples/type_name.output rename to docs/mkdocs/docs/examples/type_name.output diff --git a/docs/examples/unflatten.cpp b/docs/mkdocs/docs/examples/unflatten.cpp similarity index 100% rename from docs/examples/unflatten.cpp rename to docs/mkdocs/docs/examples/unflatten.cpp diff --git a/docs/examples/unflatten.output b/docs/mkdocs/docs/examples/unflatten.output similarity index 100% rename from docs/examples/unflatten.output rename to docs/mkdocs/docs/examples/unflatten.output diff --git a/docs/examples/update.cpp b/docs/mkdocs/docs/examples/update.cpp similarity index 100% rename from docs/examples/update.cpp rename to docs/mkdocs/docs/examples/update.cpp diff --git a/docs/examples/update.output b/docs/mkdocs/docs/examples/update.output similarity index 100% rename from docs/examples/update.output rename to docs/mkdocs/docs/examples/update.output diff --git a/docs/examples/update__range.cpp b/docs/mkdocs/docs/examples/update__range.cpp similarity index 100% rename from docs/examples/update__range.cpp rename to docs/mkdocs/docs/examples/update__range.cpp diff --git a/docs/examples/update__range.output b/docs/mkdocs/docs/examples/update__range.output similarity index 100% rename from docs/examples/update__range.output rename to docs/mkdocs/docs/examples/update__range.output diff --git a/docs/examples/value__json_ptr.cpp b/docs/mkdocs/docs/examples/value__json_ptr.cpp similarity index 100% rename from docs/examples/value__json_ptr.cpp rename to docs/mkdocs/docs/examples/value__json_ptr.cpp diff --git a/docs/examples/value__json_ptr.output b/docs/mkdocs/docs/examples/value__json_ptr.output similarity index 100% rename from docs/examples/value__json_ptr.output rename to docs/mkdocs/docs/examples/value__json_ptr.output diff --git a/docs/examples/value__keytype.c++17.cpp b/docs/mkdocs/docs/examples/value__keytype.c++17.cpp similarity index 100% rename from docs/examples/value__keytype.c++17.cpp rename to docs/mkdocs/docs/examples/value__keytype.c++17.cpp diff --git a/docs/examples/value__keytype.c++17.output b/docs/mkdocs/docs/examples/value__keytype.c++17.output similarity index 100% rename from docs/examples/value__keytype.c++17.output rename to docs/mkdocs/docs/examples/value__keytype.c++17.output diff --git a/docs/examples/value__object_t_key_type.cpp b/docs/mkdocs/docs/examples/value__object_t_key_type.cpp similarity index 100% rename from docs/examples/value__object_t_key_type.cpp rename to docs/mkdocs/docs/examples/value__object_t_key_type.cpp diff --git a/docs/examples/value__object_t_key_type.output b/docs/mkdocs/docs/examples/value__object_t_key_type.output similarity index 100% rename from docs/examples/value__object_t_key_type.output rename to docs/mkdocs/docs/examples/value__object_t_key_type.output diff --git a/docs/mkdocs/docs/examples/value__return_type.cpp b/docs/mkdocs/docs/examples/value__return_type.cpp new file mode 100644 index 0000000000..7947e1d936 --- /dev/null +++ b/docs/mkdocs/docs/examples/value__return_type.cpp @@ -0,0 +1,14 @@ +#include +#include + +using json = nlohmann::json; + +int main() +{ + json j = json::parse(R"({"uint64": 18446744073709551615})"); + + std::cout << "operator[]: " << j["uint64"] << '\n' + << "default value (int): " << j.value("uint64", 0) << '\n' + << "default value (uint64_t): " << j.value("uint64", std::uint64_t(0)) << '\n' + << "explicit return value type: " << j.value("uint64", 0) << '\n'; +} diff --git a/docs/mkdocs/docs/examples/value__return_type.output b/docs/mkdocs/docs/examples/value__return_type.output new file mode 100644 index 0000000000..7783f30fa8 --- /dev/null +++ b/docs/mkdocs/docs/examples/value__return_type.output @@ -0,0 +1,4 @@ +operator[]: 18446744073709551615 +default value (int): -1 +default value (uint64_t): 18446744073709551615 +explicit return value type: 18446744073709551615 diff --git a/docs/mkdocs/docs/features/arbitrary_types.md b/docs/mkdocs/docs/features/arbitrary_types.md index e2e7893381..1e79bfec28 100644 --- a/docs/mkdocs/docs/features/arbitrary_types.md +++ b/docs/mkdocs/docs/features/arbitrary_types.md @@ -24,9 +24,9 @@ j["age"] = p.age; // convert from JSON: copy each value from the JSON object ns::person p { - j["name"].template get(), - j["address"].template get(), - j["age"].template get() + j["name"].get(), + j["address"].get(), + j["age"].get() }; ``` @@ -43,7 +43,7 @@ std::cout << j << std::endl; // {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"} // conversion: json -> person -auto p2 = j.template get(); +auto p2 = j.get(); // that's it assert(p == p2); @@ -70,13 +70,13 @@ namespace ns { ``` That's all! When calling the `json` constructor with your type, your custom `to_json` method will be automatically called. -Likewise, when calling `template get()` or `get_to(your_type&)`, the `from_json` method will be called. +Likewise, when calling `get()` or `get_to(your_type&)`, the `from_json` method will be called. Some important things: * Those methods **MUST** be in your type's namespace (which can be the global namespace), or the library will not be able to locate them (in this example, they are in namespace `ns`, where `person` is defined). -* Those methods **MUST** be available (e.g., proper headers must be included) everywhere you use these conversions. Look at [issue 1108](https://github.com/nlohmann/json/issues/1108) for errors that may occur otherwise. -* When using `template get()`, `your_type` **MUST** be [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). (There is a way to bypass this requirement described later.) +* Those methods **MUST** be available (e.g., proper headers must be included) everywhere you use these conversions. Look at [#1108](https://github.com/nlohmann/json/issues/1108) for errors that may occur otherwise. +* When using `get()`, `your_type` **MUST** be [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). (There is a way to bypass this requirement described later.) * In function `from_json`, use function [`at()`](../api/basic_json/at.md) to access the object values rather than `operator[]`. In case a key does not exist, `at` throws an exception that you can handle, whereas `operator[]` exhibits undefined behavior. * You do not need to add serializers or deserializers for STL types like `std::vector`: the library already implements these. @@ -85,21 +85,41 @@ Some important things: If you just want to serialize/deserialize some structs, the `to_json`/`from_json` functions can be a lot of boilerplate. -There are four macros to make your life easier as long as you (1) want to use a JSON object as serialization and (2) want to use the member variable names as object keys in that object: +There are six macros to make your life easier as long as you (1) want to use a JSON object as serialization and (2) want to use the member variable names as object keys in that object: - [`NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(name, member1, member2, ...)`](../api/macros/nlohmann_define_type_non_intrusive.md) is to be defined inside the namespace of the class/struct to create code for. It will throw an exception in `from_json()` due to a missing value in the JSON object. -- [`NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(name, member1, member2, ...)`](../api/macros/nlohmann_define_type_non_intrusive.md) is to be defined inside the namespace of the class/struct to create code for. It will not throw an exception in `from_json()` due to a missing value in the JSON object, but fills in values from object which is default-constructed by the type. +- [`NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(name, member1, member2, ...)`](../api/macros/nlohmann_define_type_non_intrusive.md) is to be defined inside the namespace of the class/struct to create code for. It will not throw an exception in `from_json()` due to a missing value in the JSON object, but fills in values from an object which is default-constructed by the type. +- [`NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(name, member1, member2, ...)`](../api/macros/nlohmann_define_type_non_intrusive.md) is to be defined inside the namespace of the class/struct to create code for. It does not define a `from_json()` function which is needed in case the type does not have a default constructor. - [`NLOHMANN_DEFINE_TYPE_INTRUSIVE(name, member1, member2, ...)`](../api/macros/nlohmann_define_type_intrusive.md) is to be defined inside the class/struct to create code for. This macro can also access private members. It will throw an exception in `from_json()` due to a missing value in the JSON object. -- [`NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(name, member1, member2, ...)`](../api/macros/nlohmann_define_type_intrusive.md) is to be defined inside the class/struct to create code for. This macro can also access private members. It will not throw an exception in `from_json()` due to a missing value in the JSON object, but fills in values from object which is default-constructed by the type. - -In all macros, the first parameter is the name of the class/struct, and all remaining parameters name the members. You can read more docs about them starting from [here](macros.md#nlohmann_define_type_intrusivetype-member). +- [`NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(name, member1, member2, ...)`](../api/macros/nlohmann_define_type_intrusive.md) is to be defined inside the class/struct to create code for. This macro can also access private members. It will not throw an exception in `from_json()` due to a missing value in the JSON object, but fills in values from an object which is default-constructed by the type. +- [`NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(name, member1, member2, ...)`](../api/macros/nlohmann_define_type_intrusive.md) is to be defined inside the class/struct to create code for. This macro can also access private members. It does not define a `from_json()` function which is needed in case the type does not have a default constructor. + +Furthermore, there exist versions to use in the case of derived classes: + +| Need access to private members | Need only de-serialization | Allow missing values when de-serializing | macro | +|------------------------------------------------------------------|------------------------------------------------------------------|------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------| +|
:octicons-check-circle-fill-24:
|
:octicons-x-circle-fill-24:
|
:octicons-x-circle-fill-24:
| [**NLOHMANN_DEFINE_TYPE_INTRUSIVE**](../api/macros/nlohmann_define_type_intrusive.md) | +|
:octicons-check-circle-fill-24:
|
:octicons-x-circle-fill-24:
|
:octicons-check-circle-fill-24:
| [**NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT**](../api/macros/nlohmann_define_type_intrusive.md) | +|
:octicons-check-circle-fill-24:
|
:octicons-check-circle-fill-24:
|
:octicons-skip-fill-24:
| [**NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE**](../api/macros/nlohmann_define_type_intrusive.md) | +|
:octicons-x-circle-fill-24:
|
:octicons-x-circle-fill-24:
|
:octicons-x-circle-fill-24:
| [**NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE**](../api/macros/nlohmann_define_type_non_intrusive.md) | +|
:octicons-x-circle-fill-24:
|
:octicons-x-circle-fill-24:
|
:octicons-check-circle-fill-24:
| [**NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT**](../api/macros/nlohmann_define_type_non_intrusive.md) | +|
:octicons-x-circle-fill-24:
|
:octicons-check-circle-fill-24:
|
:octicons-skip-fill-24:
| [**NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE**](../api/macros/nlohmann_define_type_non_intrusive.md) | + +For _derived_ classes and structs, use the following macros + +| Need access to private members | Need only de-serialization | Allow missing values when de-serializing | macro | +|------------------------------------------------------------------|------------------------------------------------------------------|------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------| +|
:octicons-check-circle-fill-24:
|
:octicons-x-circle-fill-24:
|
:octicons-x-circle-fill-24:
| [**NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE**](../api/macros/nlohmann_define_derived_type.md) | +|
:octicons-check-circle-fill-24:
|
:octicons-x-circle-fill-24:
|
:octicons-check-circle-fill-24:
| [**NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT**](../api/macros/nlohmann_define_derived_type.md) | +|
:octicons-check-circle-fill-24:
|
:octicons-check-circle-fill-24:
|
:octicons-skip-fill-24:
| [**NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE**](../api/macros/nlohmann_define_derived_type.md) | +|
:octicons-x-circle-fill-24:
|
:octicons-x-circle-fill-24:
|
:octicons-x-circle-fill-24:
| [**NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE**](../api/macros/nlohmann_define_derived_type.md) | +|
:octicons-x-circle-fill-24:
|
:octicons-x-circle-fill-24:
|
:octicons-check-circle-fill-24:
| [**NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT**](../api/macros/nlohmann_define_derived_type.md) | +|
:octicons-x-circle-fill-24:
|
:octicons-check-circle-fill-24:
|
:octicons-skip-fill-24:
| [**NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE**](../api/macros/nlohmann_define_derived_type.md) | !!! info "Implementation limits" - The current macro implementations are limited to at most 64 member variables. If you want to serialize/deserialize types with more than 64 member variables, you need to define the `to_json`/`from_json` functions manually. - - The macros only work for the [`nlohmann::json`](../api/json.md) type; other specializations such as - [`nlohmann::ordered_json`](../api/ordered_json.md) are currently unsupported. ??? example @@ -129,9 +149,9 @@ In all macros, the first parameter is the name of the class/struct, and all rema ## How do I convert third-party types? -This requires a bit more advanced technique. But first, let's see how this conversion mechanism works: +This requires a bit more advanced technique. But first, let us see how this conversion mechanism works: -The library uses **JSON Serializers** to convert types to json. +The library uses **JSON Serializers** to convert types to JSON. The default serializer for `nlohmann::json` is `nlohmann::adl_serializer` (ADL means [Argument-Dependent Lookup](https://en.cppreference.com/w/cpp/language/adl)). It is implemented like this (simplified): @@ -171,7 +191,7 @@ struct adl_serializer> { if (j.is_null()) { opt = boost::none; } else { - opt = j.template get(); // same as above, but with + opt = j.get(); // same as above, but with // adl_serializer::from_json } } @@ -186,7 +206,7 @@ NLOHMANN_JSON_NAMESPACE_END ## How can I use `get()` for non-default constructible/non-copyable types? -There is a way, if your type is [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible). You will need to specialize the `adl_serializer` as well, but with a special `from_json` overload: +There is a way if your type is [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible). You will need to specialize the `adl_serializer` as well, but with a special `from_json` overload: ```cpp struct move_only_type { @@ -204,7 +224,7 @@ namespace nlohmann { // note: the return type is no longer 'void', and the method only takes // one argument static move_only_type from_json(const json& j) { - return {j.template get()}; + return {j.get()}; } // Here's the catch! You must provide a to_json method! Otherwise, you @@ -221,7 +241,7 @@ namespace nlohmann { Yes. You might want to take a look at [`unit-udt.cpp`](https://github.com/nlohmann/json/blob/develop/tests/src/unit-udt.cpp) in the test suite, to see a few examples. -If you write your own serializer, you'll need to do a few things: +If you write your own serializer, you will need to do a few things: - use a different `basic_json` alias than `nlohmann::json` (the last template parameter of `basic_json` is the `JSONSerializer`) - use your `basic_json` alias (or a template parameter) in all your `to_json`/`from_json` methods @@ -268,7 +288,7 @@ struct bad_serializer static void from_json(const BasicJsonType& j, T& value) { // this calls BasicJsonType::json_serializer::from_json(j, value); // if BasicJsonType::json_serializer == bad_serializer ... oops! - value = j.template template get(); // oops! + value = j.template get(); // oops! } }; ``` diff --git a/docs/mkdocs/docs/features/assertions.md b/docs/mkdocs/docs/features/assertions.md index 2bad62e81d..4d75d5a8ab 100644 --- a/docs/mkdocs/docs/features/assertions.md +++ b/docs/mkdocs/docs/features/assertions.md @@ -1,7 +1,7 @@ # Runtime Assertions The code contains numerous debug assertions to ensure class invariants are valid or to detect undefined behavior. -Whereas the former class invariants are nothing to be concerned of, the latter checks for undefined behavior are to +Whereas the former class invariants are nothing to be concerned with, the latter checks for undefined behavior are to detect bugs in client code. ## Switch off runtime assertions @@ -19,8 +19,8 @@ before including the `json.hpp` header. ### Unchecked object access to a const value Function [`operator[]`](../api/basic_json/operator%5B%5D.md) implements unchecked access for objects. Whereas a missing -key is added in case of non-const objects, accessing a const object with a missing key is undefined behavior (think of a -dereferenced null pointer) and yields a runtime assertion. +key is added in the case of non-const objects, accessing a const object with a missing key is undefined behavior (think +of a dereferenced null pointer) and yields a runtime assertion. If you are not sure whether an element in an object exists, use checked access with the [`at` function](../api/basic_json/at.md) or call the [`contains` function](../api/basic_json/contains.md) before. @@ -103,29 +103,42 @@ behavior and yields a runtime assertion. Assertion failed: (m_object != nullptr), function operator++, file iter_impl.hpp, line 368. ``` -### Reading from a null `FILE` pointer +## Changes -Reading from a null `#!cpp FILE` pointer is undefined behavior and yields a runtime assertion. This can happen when -calling `#!cpp std::fopen` on a nonexistent file. +### Reading from a null `FILE` or `char` pointer -??? example "Example 4: Uninitialized iterator" +Reading from a null `#!cpp FILE` or `#!cpp char` pointer in C++ is undefined behavior. Until version 3.12.0, this +library asserted that the pointer was not `nullptr` using a runtime assertion. If assertions were disabled, this would +result in undefined behavior. Since version 3.12.0, this library checks for `nullptr` and throws a +[`parse_error.101`](../home/exceptions.md#jsonexceptionparse_error101) to prevent the undefined behavior. + +??? example "Example 4: Reading from null pointer" The following code will trigger an assertion at runtime: ```cpp + #include #include using json = nlohmann::json; int main() { - std::FILE* f = std::fopen("nonexistent_file.json", "r"); - json j = json::parse(f); + std::FILE* f = std::fopen("nonexistent_file.json", "r"); + try { + json j = json::parse(f); + } catch (std::exception& e) { + std::cerr << e.what() << std::endl; + } } ``` Output: ``` - Assertion failed: (m_file != nullptr), function file_input_adapter, file input_adapters.hpp, line 55. + [json.exception.parse_error.101] parse error: attempting to parse an empty input; check that your input string or stream contains the expected JSON ``` + +## See also + +- [JSON_ASSERT](../api/macros/json_assert.md) - control behavior of runtime assertions diff --git a/docs/mkdocs/docs/features/binary_formats/bjdata.md b/docs/mkdocs/docs/features/binary_formats/bjdata.md index a89a228858..a7631baee0 100644 --- a/docs/mkdocs/docs/features/binary_formats/bjdata.md +++ b/docs/mkdocs/docs/features/binary_formats/bjdata.md @@ -2,15 +2,15 @@ The [BJData format](https://neurojson.org) was derived from and improved upon [Universal Binary JSON(UBJSON)](https://ubjson.org) specification (Draft 12). Specifically, it introduces an optimized -array container for efficient storage of N-dimensional packed arrays (**ND-arrays**); it also adds 4 new type markers - -`[u] - uint16`, `[m] - uint32`, `[M] - uint64` and `[h] - float16` - to unambiguously map common binary numeric types; -furthermore, it uses little-endian (LE) to store all numerics instead of big-endian (BE) as in UBJSON to avoid -unnecessary conversions on commonly available platforms. +array container for efficient storage of N-dimensional packed arrays (**ND-arrays**); it also adds 5 new type markers - +`[u] - uint16`, `[m] - uint32`, `[M] - uint64`, `[h] - float16` and `[B] - byte` - to unambiguously map common binary +numeric types; furthermore, it uses little-endian (LE) to store all numerics instead of big-endian (BE) as in UBJSON to +avoid unnecessary conversions on commonly available platforms. Compared to other binary JSON-like formats such as MessagePack and CBOR, both BJData and UBJSON demonstrate a rare combination of being both binary and **quasi-human-readable**. This is because all semantic elements in BJData and -UBJSON, including the data-type markers and name/string types are directly human-readable. Data stored in the -BJData/UBJSON format are not only compact in size, fast to read/write, but also can be directly searched or read using +UBJSON, including the data-type markers and name/string types, are directly human-readable. Data stored in the +BJData/UBJSON format is not only compact in size, fast to read/write, but also can be directly searched or read using simple processing. !!! abstract "References" @@ -49,6 +49,7 @@ The library uses the following mapping from JSON values types to BJData types ac | string | *with shortest length indicator* | string | `S` | | array | *see notes on optimized format/ND-array* | array | `[` | | object | *see notes on optimized format* | map | `{` | +| binary | *see notes on binary values* | array | `[$B` | !!! success "Complete mapping" @@ -114,7 +115,7 @@ The library uses the following mapping from JSON values types to BJData types ac } ``` - Likewise, when a JSON object in the above form is serialzed using + Likewise, when a JSON object in the above form is serialized using [`to_bjdata`](../../api/basic_json/to_bjdata.md), it is automatically converted into a compact BJData ND-array. The only exception is, that when the 1-dimensional vector stored in `"_ArraySize_"` contains a single integer or two integers with one being 1, a regular 1-D optimized array is generated. @@ -128,15 +129,24 @@ The library uses the following mapping from JSON values types to BJData types ac Due to diminished space saving, hampered readability, and increased security risks, in BJData, the allowed data types following the `$` marker in an optimized array and object container are restricted to - **non-zero-fixed-length** data types. Therefore, the valid optimized type markers can only be one of `UiuImlMLhdDC`. - This also means other variable (`[{SH`) or zero-length types (`TFN`) can not be used in an optimized array or object - in BJData. + **non-zero-fixed-length** data types. Therefore, the valid optimized type markers can only be one of + `UiuImlMLhdDCB`. This also means other variable (`[{SH`) or zero-length types (`TFN`) can not be used in an + optimized array or object in BJData. !!! info "Binary values" - If the JSON data contains the binary type, the value stored is a list of integers, as suggested by the BJData - documentation. In particular, this means that the serialization and the deserialization of JSON containing binary - values into BJData and back will result in a different JSON object. + BJData provides a dedicated `B` marker (defined in the [BJData specification (Draft 3)][BJDataBinArr]) that is used + in optimized arrays to designate binary data. This means that, unlike UBJSON, binary data can be both serialized and + deserialized. + + To preserve compatibility with BJData Draft 2, the Draft 3 optimized binary array must be explicitly enabled using + the `version` parameter of [`to_bjdata`](../../api/basic_json/to_bjdata.md). + + In Draft2 mode (default), if the JSON data contains the binary type, the value stored as a list of integers, as + suggested by the BJData documentation. In particular, this means that the serialization and the deserialization of + JSON containing binary values into BJData and back will result in a different JSON object. + + [BJDataBinArr]: https://github.com/NeuroJSON/bjdata/blob/master/Binary_JData_Specification.md#optimized-binary-array) ??? example @@ -154,28 +164,30 @@ The library uses the following mapping from JSON values types to BJData types ac The library maps BJData types to JSON value types as follows: -| BJData type | JSON value type | marker | -|-------------|-----------------------------------------|--------| -| no-op | *no value, next value is read* | `N` | -| null | `null` | `Z` | -| false | `false` | `F` | -| true | `true` | `T` | -| float16 | number_float | `h` | -| float32 | number_float | `d` | -| float64 | number_float | `D` | -| uint8 | number_unsigned | `U` | -| int8 | number_integer | `i` | -| uint16 | number_unsigned | `u` | -| int16 | number_integer | `I` | -| uint32 | number_unsigned | `m` | -| int32 | number_integer | `l` | -| uint64 | number_unsigned | `M` | -| int64 | number_integer | `L` | -| string | string | `S` | -| char | string | `C` | -| array | array (optimized values are supported) | `[` | -| ND-array | object (in JData annotated array format)|`[$.#[.`| -| object | object (optimized values are supported) | `{` | +| BJData type | JSON value type | marker | +|-------------|------------------------------------------|----------| +| no-op | *no value, next value is read* | `N` | +| null | `null` | `Z` | +| false | `false` | `F` | +| true | `true` | `T` | +| float16 | number_float | `h` | +| float32 | number_float | `d` | +| float64 | number_float | `D` | +| uint8 | number_unsigned | `U` | +| int8 | number_integer | `i` | +| uint16 | number_unsigned | `u` | +| int16 | number_integer | `I` | +| uint32 | number_unsigned | `m` | +| int32 | number_integer | `l` | +| uint64 | number_unsigned | `M` | +| int64 | number_integer | `L` | +| byte | number_unsigned | `B` | +| string | string | `S` | +| char | string | `C` | +| array | array (optimized values are supported) | `[` | +| ND-array | object (in JData annotated array format) | `[$.#[.` | +| object | object (optimized values are supported) | `{` | +| binary | binary (strongly-typed byte array) | `[$B` | !!! success "Complete mapping" diff --git a/docs/mkdocs/docs/features/binary_formats/bson.md b/docs/mkdocs/docs/features/binary_formats/bson.md index f3b8cf18d0..cf87731a0f 100644 --- a/docs/mkdocs/docs/features/binary_formats/bson.md +++ b/docs/mkdocs/docs/features/binary_formats/bson.md @@ -23,7 +23,7 @@ The library uses the following mapping from JSON values types to BSON types: | number_integer | 2147483648..9223372036854775807 | int64 | 0x12 | | number_unsigned | 0..2147483647 | int32 | 0x10 | | number_unsigned | 2147483648..9223372036854775807 | int64 | 0x12 | -| number_unsigned | 9223372036854775808..18446744073709551615 | -- | -- | +| number_unsigned | 9223372036854775808..18446744073709551615 | uint64 | 0x11 | | number_float | *any value* | double | 0x01 | | string | *any value* | string | 0x02 | | array | *any value* | document | 0x04 | @@ -32,11 +32,8 @@ The library uses the following mapping from JSON values types to BSON types: !!! warning "Incomplete mapping" - The mapping is **incomplete**, since only JSON-objects (and things - contained therein) can be serialized to BSON. - Also, integers larger than 9223372036854775807 cannot be serialized to BSON, - and the keys may not contain U+0000, since they are serialized a - zero-terminated c-strings. + The mapping is **incomplete**, since only JSON-objects (and things contained therein) can be serialized to BSON. + Also, keys may not contain U+0000, since they are serialized a zero-terminated c-strings. ??? example @@ -73,7 +70,7 @@ The library maps BSON record types to JSON value types as follows: | Symbol | 0x0E | *unsupported* | | JavaScript Code | 0x0F | *unsupported* | | int32 | 0x10 | number_integer | -| Timestamp | 0x11 | *unsupported* | +| uint64(Timestamp) | 0x11 | number_unsigned | | 128-bit decimal float | 0x13 | *unsupported* | | Max Key | 0x7F | *unsupported* | | Min Key | 0xFF | *unsupported* | @@ -82,6 +79,10 @@ The library maps BSON record types to JSON value types as follows: The mapping is **incomplete**. The unsupported mappings are indicated in the table above. +!!! note "Handling of BSON type 0x11" + + BSON type 0x11 is used to represent uint64 numbers. This library treats these values purely as uint64 numbers + and does not parse them into date-related formats. ??? example diff --git a/docs/mkdocs/docs/features/binary_formats/cbor.md b/docs/mkdocs/docs/features/binary_formats/cbor.md index 2d0a1dae10..a4240ffad2 100644 --- a/docs/mkdocs/docs/features/binary_formats/cbor.md +++ b/docs/mkdocs/docs/features/binary_formats/cbor.md @@ -1,7 +1,7 @@ # CBOR -The Concise Binary Object Representation (CBOR) is a data format whose design goals include the possibility of extremely -small code size, fairly small message size, and extensibility without the need for version negotiation. +The Concise Binary Object Representation (CBOR) is a data format whose design goals include the possibility of +extremely small code sizes, fairly small message size, and extensibility without the need for version negotiation. !!! abstract "References" diff --git a/docs/mkdocs/docs/features/binary_formats/index.md b/docs/mkdocs/docs/features/binary_formats/index.md index e74290b097..6714965bd8 100644 --- a/docs/mkdocs/docs/features/binary_formats/index.md +++ b/docs/mkdocs/docs/features/binary_formats/index.md @@ -1,6 +1,6 @@ # Binary Formats -Though JSON is a ubiquitous data format, it is not a very compact format suitable for data exchange, for instance over +Though JSON is a ubiquitous data format, it is not a very compact format suitable for data exchange, for instance, over a network. Hence, the library supports - [BJData](bjdata.md) (Binary JData), diff --git a/docs/mkdocs/docs/features/binary_values.md b/docs/mkdocs/docs/features/binary_values.md index 5ad6433cff..260d67b54f 100644 --- a/docs/mkdocs/docs/features/binary_values.md +++ b/docs/mkdocs/docs/features/binary_values.md @@ -1,26 +1,28 @@ # Binary Values The library implements several [binary formats](binary_formats/index.md) that encode JSON in an efficient way. Most of -these formats support binary values; that is, values that have semantics define outside the library and only define a +these formats support binary values; that is, values that have semantics defined outside the library and only define a sequence of bytes to be stored. JSON itself does not have a binary value. As such, binary values are an extension that this library implements to store -values received by a binary format. Binary values are never created by the JSON parser, and are only part of a +values received by a binary format. Binary values are never created by the JSON parser and are only part of a serialized JSON text if they have been created manually or via a binary format. ## API for binary values -```plantuml -class json::binary_t { - -- setters -- +```mermaid +classDiagram + +class binary_t ["json::binary_t"] { +void set_subtype(std::uint64_t subtype) +void clear_subtype() - -- getters -- +std::uint64_t subtype() const +bool has_subtype() const } -"std::vector" <|-- json::binary_t +class vector ["std::vector"] + +vector <|-- binary_t ``` By default, binary values are stored as `std::vector`. This type can be changed by providing a template @@ -128,8 +130,8 @@ is an integer or `null`. ### BJData -[BJData](binary_formats/bjdata.md) neither supports binary values nor subtypes, and proposes to serialize binary values -as array of uint8 values. This translation is implemented by the library. +[BJData](binary_formats/bjdata.md) neither supports binary values nor subtypes and proposes to serialize binary values +as an array of uint8 values. The library implements this translation. ??? example @@ -187,7 +189,7 @@ as array of uint8 values. This translation is implemented by the library. ### BSON -[BSON](binary_formats/bson.md) supports binary values and subtypes. If a subtype is given, it is used and added as +[BSON](binary_formats/bson.md) supports binary values and subtypes. If a subtype is given, it is used and added as an unsigned 8-bit integer. If no subtype is given, the generic binary subtype 0x00 is used. ??? example @@ -272,7 +274,7 @@ byte array. [MessagePack](binary_formats/messagepack.md) supports binary values and subtypes. If a subtype is given, the ext family is used. The library will choose the smallest representation among fixext1, fixext2, fixext4, fixext8, ext8, ext16, and -ext32. The subtype is then added as signed 8-bit integer. +ext32. The subtype is then added as a signed 8-bit integer. If no subtype is given, the bin family (bin8, bin16, bin32) is used. @@ -313,8 +315,8 @@ If no subtype is given, the bin family (bin8, bin16, bin32) is used. ### UBJSON -[UBJSON](binary_formats/ubjson.md) neither supports binary values nor subtypes, and proposes to serialize binary values -as array of uint8 values. This translation is implemented by the library. +[UBJSON](binary_formats/ubjson.md) neither supports binary values nor subtypes and proposes to serialize binary values +as an array of uint8 values. The library implements this translation. ??? example diff --git a/docs/mkdocs/docs/features/comments.md b/docs/mkdocs/docs/features/comments.md index 61266d9caf..6876cc508c 100644 --- a/docs/mkdocs/docs/features/comments.md +++ b/docs/mkdocs/docs/features/comments.md @@ -5,13 +5,15 @@ This library does not support comments *by default*. It does so for three reason 1. Comments are not part of the [JSON specification](https://tools.ietf.org/html/rfc8259). You may argue that `//` or `/* */` are allowed in JavaScript, but JSON is not JavaScript. 2. This was not an oversight: Douglas Crockford [wrote on this](https://plus.google.com/118095276221607585885/posts/RK8qyGVaGSr) in May 2012: - > I removed comments from JSON because I saw people were using them to hold parsing directives, a practice which would have destroyed interoperability. I know that the lack of comments makes some people sad, but it shouldn't. + > I removed comments from JSON because I saw people were using them to hold parsing directives, a practice which would have destroyed interoperability. I know that the lack of comments makes some people sad, but it shouldn't. - > Suppose you are using JSON to keep configuration files, which you would like to annotate. Go ahead and insert all the comments you like. Then pipe it through JSMin before handing it to your JSON parser. + > Suppose you are using JSON to keep configuration files, which you would like to annotate. Go ahead and insert all the comments you like. Then pipe it through JSMin before handing it to your JSON parser. -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. +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 - #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: ' { /'; expected string literal - ``` - - ```json - { - "planets": [ - "Mercury", - "Venus", - "Earth", - "Mars", - "Jupiter", - "Uranus", - "Neptune" - ] - } + --8<-- "examples/comments.output" ``` diff --git a/docs/mkdocs/docs/features/element_access/default_value.md b/docs/mkdocs/docs/features/element_access/default_value.md index 02b4fea3ff..2603a2d18e 100644 --- a/docs/mkdocs/docs/features/element_access/default_value.md +++ b/docs/mkdocs/docs/features/element_access/default_value.md @@ -2,7 +2,11 @@ ## Overview -In many situations such as configuration files, missing values are not exceptional, but may be treated as if a default value was present. +In many situations, such as configuration files, missing values are not exceptional, but may be treated as if a default +value was present. For this case, use [`value(key, default_value)`](../../api/basic_json/value.md) which takes the key +you want to access and a default value in case there is no value stored with that key. + +## Example ??? example @@ -17,16 +21,43 @@ In many situations such as configuration files, missing values are not exception Assume the value is parsed to a `json` variable `j`. - | expression | value | - | ---------- | ----- | - | `#!cpp j` | `#!json {"logOutput": "result.log", "append": true}` | - | `#!cpp j.value("logOutput", "logfile.log")` | `#!json "result.log"` | - | `#!cpp j.value("append", true)` | `#!json true` | - | `#!cpp j.value("append", false)` | `#!json true` | - | `#!cpp j.value("logLevel", "verbose")` | `#!json "verbose"` | + | expression | value | + |---------------------------------------------|------------------------------------------------------| + | `#!cpp j` | `#!json {"logOutput": "result.log", "append": true}` | + | `#!cpp j.value("logOutput", "logfile.log")` | `#!json "result.log"` | + | `#!cpp j.value("append", true)` | `#!json true` | + | `#!cpp j.value("append", false)` | `#!json true` | + | `#!cpp j.value("logLevel", "verbose")` | `#!json "verbose"` | -## Note +## Notes !!! failure "Exceptions" - `value` can only be used with objects. For other types, a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error306) is thrown. + +!!! warning "Return type" + + The value function is a template, and the return type of the function is determined by the type of the provided + default value unless otherwise specified. This can have unexpected effects. In the example below, we store a 64-bit + unsigned integer. We get exactly that value when using [`operator[]`](../../api/basic_json/operator[].md). However, + when we call `value` and provide `#!c 0` as default value, then `#!c -1` is returned. The occurs, because `#!c 0` + has type `#!c int` which overflows when handling the value `#!c 18446744073709551615`. + + To address this issue, either provide a correctly typed default value or use the template parameter to specify the + desired return type. Note that this issue occurs even when a value is stored at the provided key, and the default + value is not used as the return value. + + ```cpp + --8<-- "examples/value__return_type.cpp" + ``` + + Output: + + ```json + --8<-- "examples/value__return_type.output" + ``` + +## See also + +- [`value`](../../api/basic_json/value.md) for access with default value +- documentation on [checked access](checked_access.md) diff --git a/docs/mkdocs/docs/features/element_access/unchecked_access.md b/docs/mkdocs/docs/features/element_access/unchecked_access.md index 39f06dc9f2..f2de067d36 100644 --- a/docs/mkdocs/docs/features/element_access/unchecked_access.md +++ b/docs/mkdocs/docs/features/element_access/unchecked_access.md @@ -29,7 +29,7 @@ similar to a `#!cpp std::map` and a `#!cpp std::vector`, respectively. | `#!cpp j["hobbies"][1]` | `#!json "reading"` | The return value is a reference, so it can modify the original value. In case the passed object key is non-existing, a -`#!json null` value is inserted which can be immediately be overwritten. +`#!json null` value is inserted which can immediately be overwritten. ??? example "Write access" diff --git a/docs/mkdocs/docs/features/enum_conversion.md b/docs/mkdocs/docs/features/enum_conversion.md index 1755bca2ad..bd3977d919 100644 --- a/docs/mkdocs/docs/features/enum_conversion.md +++ b/docs/mkdocs/docs/features/enum_conversion.md @@ -1,8 +1,8 @@ # Specializing enum conversion -By default, enum values are serialized to JSON as integers. In some cases this could result in undesired behavior. If an -enum is modified or re-ordered after data has been serialized to JSON, the later de-serialized JSON data may be -undefined or a different enum value than was originally intended. +By default, enum values are serialized to JSON as integers. In some cases, this could result in undesired behavior. If +the integer values of any enum values are changed after data using those enum values has been serialized to JSON, then +deserializing that JSON would result in a different enum value being restored, or the value not being found at all. It is possible to more precisely specify how a given enum is mapped to and from JSON as shown below: @@ -36,11 +36,11 @@ assert(j == "stopped"); // json string to enum json j3 = "running"; -assert(j3.template get() == TS_RUNNING); +assert(j3.get() == TS_RUNNING); // undefined json value to enum (where the first map entry above is the default) json jPi = 3.14; -assert(jPi.template get() == TS_INVALID ); +assert(jPi.get() == TS_INVALID ); ``` ## Notes @@ -54,7 +54,7 @@ Just as in [Arbitrary Type Conversions](arbitrary_types.md) above, Other Important points: -- When using `template get()`, undefined JSON values will default to the first pair specified in your map. Select this +- When using `get()`, undefined JSON values will default to the first pair specified in your map. Select this default pair carefully. - If an enum or JSON value is specified more than once in your map, the first matching occurrence from the top of the map will be returned when converting to or from JSON. diff --git a/docs/mkdocs/docs/features/iterators.md b/docs/mkdocs/docs/features/iterators.md index ce627e0126..6d8afc7f5a 100644 --- a/docs/mkdocs/docs/features/iterators.md +++ b/docs/mkdocs/docs/features/iterators.md @@ -37,7 +37,7 @@ When iterating over objects, values are ordered with respect to the `object_comp The reason for the order is the lexicographic ordering of the object keys "one", "three", "two". -### Access object key during iteration +### Access object keys during iteration The JSON iterators have two member functions, `key()` and `value()` to access the object key and stored value, respectively. When calling `key()` on a non-object iterator, an [invalid_iterator.207](../home/exceptions.md#jsonexceptioninvalid_iterator207) exception is thrown. @@ -100,7 +100,7 @@ for (auto& [key, val] : j_object.items()) !!! warning - Using `items()` on temporary objects is dangerous. Make sure the object's lifetime exceeds the iteration. See for more information. + Using `items()` on temporary objects is dangerous. Make sure the object's lifetime exceeds the iteration. See [#2040](https://github.com/nlohmann/json/issues/2040) for more information. ### Reverse iteration order diff --git a/docs/mkdocs/docs/features/json_pointer.md b/docs/mkdocs/docs/features/json_pointer.md index 4fd58f20e7..a16e8df248 100644 --- a/docs/mkdocs/docs/features/json_pointer.md +++ b/docs/mkdocs/docs/features/json_pointer.md @@ -2,8 +2,8 @@ ## Introduction -The library supports **JSON Pointer** ([RFC 6901](https://tools.ietf.org/html/rfc6901)) as alternative means to address -structured values. A JSON Pointer is a string that identifies a specific value within a JSON document. +The library supports **JSON Pointer** ([RFC 6901](https://tools.ietf.org/html/rfc6901)) as an alternative means to +address structured values. A JSON Pointer is a string that identifies a specific value within a JSON document. Consider the following JSON document @@ -42,7 +42,7 @@ Note `/` does not identify the root (i.e., the whole document), but an object en JSON Pointers can be created from a string: ```cpp -json::json_pointer p = "/nested/one"; +json::json_pointer p("/nested/one"); ``` Furthermore, a user-defined string literal can be used to achieve the same result: diff --git a/docs/mkdocs/docs/features/macros.md b/docs/mkdocs/docs/features/macros.md index 926741b091..a600edbdb5 100644 --- a/docs/mkdocs/docs/features/macros.md +++ b/docs/mkdocs/docs/features/macros.md @@ -24,11 +24,26 @@ When enabled, exception messages contain a [JSON Pointer](json_pointer.md) to th exception, see [Extended diagnostic messages](../home/exceptions.md#extended-diagnostic-messages) for an example. Note that enabling this macro increases the size of every JSON value by one pointer and adds some runtime overhead. -The diagnostics messages can also be controlled with the CMake option `JSON_Diagnostics` (`OFF` by default) which sets -`JSON_DIAGNOSTICS` accordingly. +The diagnostics messages can also be controlled with the CMake option +[`JSON_Diagnostics`](../integration/cmake.md#json_diagnostics) (`OFF` by default) which sets `JSON_DIAGNOSTICS` +accordingly. See [full documentation of `JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md). +## `JSON_DIAGNOSTIC_POSITIONS` + +When enabled, two new member functions [`start_pos()`](../api/basic_json/start_pos.md) and +[`end_pos()`](../api/basic_json/end_pos.md) are added to [`basic_json`](../api/basic_json/index.md) values. If the value +was created by calling the[`parse`](../api/basic_json/parse.md) function, then these functions allow querying the byte +positions of the value in the input it was parsed from. The byte positions are also used in exceptions to help locate +errors. + +The diagnostics positions can also be controlled with the CMake option +[`JSON_Diagnostic_Positions`](../integration/cmake.md#json_diagnostic_positions) (`OFF` by default) which sets +`JSON_DIAGNOSTIC_POSITIONS` accordingly. + +See [full documentation of `JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md) + ## `JSON_HAS_CPP_11`, `JSON_HAS_CPP_14`, `JSON_HAS_CPP_17`, `JSON_HAS_CPP_20` The library targets C++11, but also supports some features introduced in later C++ versions (e.g., `std::string_view` @@ -77,8 +92,8 @@ See [full documentation of `JSON_SKIP_LIBRARY_VERSION_CHECK`](../api/macros/json ## `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK` -When defined, the library will not create a compile error when a known unsupported compiler is detected. This allows to -use the library with compilers that do not fully support C++11 and may only work if unsupported features are not used. +When defined, the library will not create a compile error when a known unsupported compiler is detected. This allows +using the library with compilers that do not fully support C++11 and may only work if unsupported features are not used. See [full documentation of `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK`](../api/macros/json_skip_unsupported_compiler_check.md). @@ -100,57 +115,10 @@ When defined to `0`, implicit conversions are switched off. By default, implicit See [full documentation of `JSON_USE_IMPLICIT_CONVERSIONS`](../api/macros/json_use_implicit_conversions.md). -## `NLOHMANN_DEFINE_TYPE_INTRUSIVE(type, member...)` - -This macro can be used to simplify the serialization/deserialization of types if (1) want to use a JSON object as -serialization and (2) want to use the member variable names as object keys in that object. - -The macro is to be defined inside the class/struct to create code for. Unlike -[`NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`](#nlohmann_define_type_non_intrusivetype-member), it can access private members. -The first parameter is the name of the class/struct, and all remaining parameters name the members. - -See [full documentation of `NLOHMANN_DEFINE_TYPE_INTRUSIVE`](../api/macros/nlohmann_define_type_intrusive.md). - -## `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(type, member...)` - -This macro is similar to `NLOHMANN_DEFINE_TYPE_INTRUSIVE`. It will not throw an exception in `from_json()` due to a -missing value in the JSON object, but can throw due to a mismatched type. The `from_json()` function default constructs -an object and uses its values as the defaults when calling the [`value`](../api/basic_json/value.md) function. - -See [full documentation of `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT`](../api/macros/nlohmann_define_type_intrusive.md). - -## `NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(type, member...)` - -This macro is similar to `NLOHMANN_DEFINE_TYPE_INTRUSIVE` except that it defines only the serialization code. This is -useful when the user type does not have a default constructor and only the serialization is required. - -See [full documentation of `NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE`](../api/macros//nlohmann_define_type_intrusive.md). - -## `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(type, member...)` - -This macro can be used to simplify the serialization/deserialization of types if (1) want to use a JSON object as -serialization and (2) want to use the member variable names as object keys in that object. - -The macro is to be defined inside the namespace of the class/struct to create code for. Private members cannot be -accessed. Use [`NLOHMANN_DEFINE_TYPE_INTRUSIVE`](#nlohmann_define_type_intrusivetype-member) in these scenarios. The -first parameter is the name of the class/struct, and all remaining parameters name the members. - -See [full documentation of `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`](../api/macros/nlohmann_define_type_non_intrusive.md). - -## `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(type, member...)` - -This macro is similar to `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`. It will not throw an exception in `from_json()` due to a -missing value in the JSON object, but can throw due to a mismatched type. The `from_json()` function default constructs -an object and uses its values as the defaults when calling the [`value`](../api/basic_json/value.md) function. - -See [full documentation of `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT`](../api/macros/nlohmann_define_type_non_intrusive.md). - -## `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(type, member...)` - -This macro is similar to `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE` except that it defines only the serialization code. This is -useful when the user type does not have a default constructor and only the serialization is required. +## `NLOHMANN_DEFINE_TYPE_*(...)`, `NLOHMANN_DEFINE_DERIVED_TYPE_*(...)` -See [full documentation of `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE`](../api/macros//nlohmann_define_type_non_intrusive.md). +The library defines 12 macros to simplify the serialization/deserialization of types. See the page on +[arbitrary type conversion](arbitrary_types.md#simplify-your-life-with-macros) for a detailed discussion. ## `NLOHMANN_JSON_SERIALIZE_ENUM(type, ...)` diff --git a/docs/mkdocs/docs/features/modules.md b/docs/mkdocs/docs/features/modules.md new file mode 100644 index 0000000000..6068108b8b --- /dev/null +++ b/docs/mkdocs/docs/features/modules.md @@ -0,0 +1,34 @@ +# Modules + +This library has experimental support for C++ modules, introduced in C++20. The library can be imported by writing `import nlohmann.json;` instead of `#include `. + +Please be aware that the module is experimental and a full test is outstanding, and the exported symbols are subject to change. + +## Requirements +The `nlohmann.json` module requires that the build system is configured to build and resolve modules when imported. Obviously, as modules were introduced in C++20, this feature can only be used in C++20 and subsequent versions. + +To enable building the `nlohmann.json` module (which is not done by default), the macro `NLOHMANN_JSON_BUILD_MODULES` must be passed to the build system. + +## Example +When using modules rather than headers, the previous example for creating a `json` object through a JSON file, would instead be: +```cpp +import std; +import nlohmann.json; + +using json = nlohmann::json; + +// ... + +std::ifstream f("example.json"); +json data = json::parse(f); +``` + +## Modules do not export macros +It should be noted that as modules do not export macros, the `nlohmann.json` module will not export any macros, but rather only the following symbols: + +- `nlohmann::adl_serializer` +- `nlohmann::basic_json` +- `nlohmann::json` +- `nlohmann::json_pointer` +- `nlohmann::ordered_map` +- `nlohmann::ordered_json` diff --git a/docs/mkdocs/docs/features/namespace.md b/docs/mkdocs/docs/features/namespace.md index 8cee2ccfe7..1a147ec30f 100644 --- a/docs/mkdocs/docs/features/namespace.md +++ b/docs/mkdocs/docs/features/namespace.md @@ -10,7 +10,7 @@ types. The complete default namespace name is derived as follows: - The root namespace is always `nlohmann`. -- The inline namespace starts with `json_abi` and is followed by serveral optional ABI tags according to the value of +- The inline namespace starts with `json_abi` and is followed by several optional ABI tags according to the value of these ABI-affecting macros, in order: - [`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md) defined non-zero appends `_diag`. - [`JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON`](../api/macros/json_use_legacy_discarded_value_comparison.md) @@ -30,15 +30,16 @@ nlohmann::json_abi_diag_v3_11_2 Several incompatibilities have been observed. Amongst the most common ones is linking code compiled with different definitions of [`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md). This is illustrated in the diagram below. -```plantuml -[**nlohmann_json (v3.10.5)**\nJSON_DIAGNOSTICS=0] as [json] -[**nlohmann_json (v3.10.5)**\nJSON_DIAGNOSTICS=1] as [json_diag] -[**some_library**] as [library] -[**application**] as [app] - -[library] ..|> [json] -[app] ..|> [json_diag] -[app] ..|>[library] +```mermaid +graph + json["nlohmann_json (v3.10.5)
JSON_DIAGNOSTICS=0"] + json_diag["nlohmann_json (v3.10.5)
JSON_DIAGNOSTICS=1"] + library["some library"] + app["application"] + + library --> json + app --> json_diag + app --> library ``` In releases prior to 3.11.0, mixing any version of the JSON library with different `JSON_DIAGNOSTICS` settings would @@ -63,7 +64,7 @@ configurations – to be used in cases where the linker would otherwise output u To do so, define [`NLOHMANN_JSON_NAMESPACE_NO_VERSION`](../api/macros/nlohmann_json_namespace_no_version.md) to `1`. -This applies to version 3.11.2 and above only, versions 3.11.0 and 3.11.1 can apply the technique described in the next +This applies to version 3.11.2 and above only; versions 3.11.0 and 3.11.1 can apply the technique described in the next section to emulate the effect of the `NLOHMANN_JSON_NAMESPACE_NO_VERSION` macro. !!! danger "Use at your own risk" diff --git a/docs/mkdocs/docs/features/parsing/sax_interface.md b/docs/mkdocs/docs/features/parsing/sax_interface.md index 0796a55f52..cfea680a40 100644 --- a/docs/mkdocs/docs/features/parsing/sax_interface.md +++ b/docs/mkdocs/docs/features/parsing/sax_interface.md @@ -2,27 +2,30 @@ The library uses a SAX-like interface with the following functions: -```plantuml -interface json::sax_t { - + {abstract} bool null() - - + {abstract} bool boolean(bool val) - - + {abstract} bool number_integer(number_integer_t val) - + {abstract} bool number_unsigned(number_unsigned_t val) - - + {abstract} bool number_float(number_float_t val, const string_t& s) - - + {abstract} bool string(string_t& val) - + {abstract} bool binary(binary_t& val) - - + {abstract} bool start_object(std::size_t elements) - + {abstract} bool end_object() - + {abstract} bool start_array(std::size_t elements) - + {abstract} bool end_array() - + {abstract} bool key(string_t& val) - - + {abstract} bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex) +```mermaid +classDiagram + +class sax_t ["json::sax_t"] { + <> + +bool null()* + + +bool boolean(bool val)* + + +bool number_integer(number_integer_t val)* + +bool number_unsigned(number_unsigned_t val)* + + +bool number_float(number_float_t val, const string_t& s)* + + +bool string(string_t& val)* + +bool binary(binary_t& val)* + + +bool start_object(std::size_t elements)* + +bool end_object()* + +bool start_array(std::size_t elements)* + +bool end_array()* + +bool key(string_t& val)* + + +bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex)* } ``` @@ -43,7 +46,7 @@ bool number_float(number_float_t val, const string_t& s); // called when a string is parsed; value is passed and can be safely moved away bool string(string_t& val); // called when a binary value is parsed; value is passed and can be safely moved away -bool binary(binary& val); +bool binary(binary_t& val); // called when an object or array begins or ends, resp. The number of elements is passed (or -1 if not known) bool start_object(std::size_t elements); diff --git a/docs/mkdocs/docs/features/trailing_commas.md b/docs/mkdocs/docs/features/trailing_commas.md new file mode 100644 index 0000000000..583e7cbffc --- /dev/null +++ b/docs/mkdocs/docs/features/trailing_commas.md @@ -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" + ``` diff --git a/docs/mkdocs/docs/features/types/index.md b/docs/mkdocs/docs/features/types/index.md index d9dfcc29a0..56fb7ebe9e 100644 --- a/docs/mkdocs/docs/features/types/index.md +++ b/docs/mkdocs/docs/features/types/index.md @@ -1,6 +1,6 @@ # Types -This page gives an overview how JSON values are stored and how this can be configured. +This page gives an overview of how JSON values are stored and how this can be configured. ## Overview @@ -19,8 +19,11 @@ Note there are three different types for numbers - when parsing JSON text, the b ## Storage -```plantuml -enum value_t { +```mermaid +classDiagram + +class value_t { + <> null object array @@ -33,7 +36,8 @@ enum value_t { discarded } -class json_value << (U,orchid) >> { +class json_value { + <> object_t* object array_t* array string_t* string @@ -45,17 +49,15 @@ class json_value << (U,orchid) >> { } class basic_json { - -- type and value -- - value_t m_type - json_value m_value - -- derived types -- - + typedef object_t - + typedef array_t - + typedef binary_t - + typedef boolean_t - + typedef number_integer_t - + typedef number_unsigned_t - + typedef number_float_t + -value_t m_type + -json_value m_value + +typedef object_t + +typedef array_t + +typedef binary_t + +typedef boolean_t + +typedef number_integer_t + +typedef number_unsigned_t + +typedef number_float_t } basic_json .. json_value @@ -135,7 +137,7 @@ The choice of `object_t` influences the behavior of the JSON class. With the def ### Key order -The order name/value pairs are added to the object is *not* preserved by the library. Therefore, iterating an object may return name/value pairs in a different order than they were originally stored. In fact, keys will be traversed in alphabetical order as `std::map` with `std::less` is used by default. Please note this behavior conforms to [RFC 8259](https://tools.ietf.org/html/rfc8259), because any order implements the specified "unordered" nature of JSON objects. +The order name/value pairs are added to the object are *not* preserved by the library. Therefore, iterating an object may return name/value pairs in a different order than they were originally stored. In fact, keys will be traversed in alphabetical order as `std::map` with `std::less` is used by default. Please note this behavior conforms to [RFC 8259](https://tools.ietf.org/html/rfc8259), because any order implements the specified "unordered" nature of JSON objects. ### Limits @@ -200,7 +202,7 @@ Strings are stored in UTF-8 encoding. Therefore, functions like `std::string::si [RFC 8259](https://tools.ietf.org/html/rfc8259) states: -> Software implementations are typically required to test names of object members for equality. Implementations that transform the textual representation into sequences of Unicode code units and then perform the comparison numerically, code unit by code unit, are interoperable in the sense that implementations will agree in all cases on equality or inequality of two strings. For example, implementations that compare strings with escaped characters unconverted may incorrectly find that `"a\\b"` and `"a\u005Cb"` are not equal. +> Software implementations are typically required to test names of object members for equality. Implementations that transform the textual representation into sequences of Unicode code units and then perform the comparison numerically, code unit by code unit are interoperable in the sense that implementations will agree in all cases on equality or inequality of two strings. For example, implementations that compare strings with escaped characters unconverted may incorrectly find that `"a\\b"` and `"a\u005Cb"` are not equal. This implementation is interoperable as it does compare strings code unit by code unit. @@ -229,7 +231,7 @@ See the [number handling](number_handling.md) article for a detailed discussion > The representation of numbers is similar to that used in most programming languages. A number is represented in base 10 using decimal digits. It contains an integer component that may be prefixed with an optional minus sign, which may be followed by a fraction part and/or an exponent part. Leading zeros are not allowed. (...) Numeric values that cannot be represented in the grammar below (such as Infinity and NaN) are not permitted. -This description includes both integer and floating-point numbers. However, C++ allows more precise storage if it is known whether the number is a signed integer, an unsigned integer or a floating-point number. Therefore, three different types, `number_integer_t`, `number_unsigned_t`, and `number_float_t` are used. +This description includes both integer and floating-point numbers. However, C++ allows more precise storage if it is known whether the number is a signed integer, an unsigned integer, or a floating-point number. Therefore, three different types, `number_integer_t`, `number_unsigned_t`, and `number_float_t` are used. ### Default types @@ -239,7 +241,7 @@ With the default values for *NumberFloatType* (`#!cpp double`), the default valu ### Default behavior -- The restrictions about leading zeros is not enforced in C++. Instead, leading zeros in integer literals lead to an interpretation as octal number. Internally, the value will be stored as decimal number. For instance, the C++ integer literal `#!c 010` will be serialized to `#!c 8`. During deserialization, leading zeros yield an error. +- The restrictions about leading zeros are not enforced in C++. Instead, leading zeros in integer literals lead to an interpretation as an octal number. Internally, the value will be stored as a decimal number. For instance, the C++ integer literal `#!c 010` will be serialized to `#!c 8`. During deserialization, leading zeros yield an error. - Not-a-number (NaN) values will be serialized to `#!json null`. ### Limits @@ -248,9 +250,9 @@ With the default values for *NumberFloatType* (`#!cpp double`), the default valu > An implementation may set limits on the range and precision of numbers. -When the default type is used, the maximal integer number that can be stored is `#!c 9223372036854775807` (`INT64_MAX`) and the minimal integer number that can be stored is `#!c -9223372036854775808` (`INT64_MIN`). Integer numbers that are out of range will yield over/underflow when used in a constructor. During deserialization, too large or small integer numbers will be automatically be stored as `number_unsigned_t` or `number_float_t`. +When the default type is used, the maximal integer number that can be stored is `#!c 9223372036854775807` (`INT64_MAX`) and the minimal integer number that can be stored is `#!c -9223372036854775808` (`INT64_MIN`). Integer numbers that are out of range will yield over/underflow when used in a constructor. During deserialization, too large or small integer numbers will automatically be stored as `number_unsigned_t` or `number_float_t`. -When the default type is used, the maximal unsigned integer number that can be stored is `#!c 18446744073709551615` (`UINT64_MAX`) and the minimal integer number that can be stored is `#!c 0`. Integer numbers that are out of range will yield over/underflow when used in a constructor. During deserialization, too large or small integer numbers will be automatically be stored as `number_integer_t` or `number_float_t`. +When the default type is used, the maximal unsigned integer number that can be stored is `#!c 18446744073709551615` (`UINT64_MAX`) and the minimal integer number that can be stored is `#!c 0`. Integer numbers that are out of range will yield over/underflow when used in a constructor. During deserialization, too large or small integer numbers will automatically be stored as `number_integer_t` or `number_float_t`. [RFC 8259](https://tools.ietf.org/html/rfc8259) further states: diff --git a/docs/mkdocs/docs/features/types/number_handling.md b/docs/mkdocs/docs/features/types/number_handling.md index 3dcca76a4c..9102376bb9 100644 --- a/docs/mkdocs/docs/features/types/number_handling.md +++ b/docs/mkdocs/docs/features/types/number_handling.md @@ -54,13 +54,13 @@ On number interoperability, the following remarks are made: ## Library implementation -This section describes how the above number specification is implemented by this library. +This section describes how this library implements the above number specification. ### Number storage In the default [`json`](../../api/json.md) type, numbers are stored as `#!c std::uint64_t`, `#!c std::int64_t`, and -`#!c double`, respectively. Thereby, `#!c std::uint64_t` and `#!c std::int64_t` are used only if they can store the -number without loss of precision. If this is impossible (e.g., if the number is too large), the number is stored as +`#!c double`, respectively. Thereby, `#!c std::uint64_t` and `#!c std::int64_t` are used only if they can store the +number without loss of precision. If this is impossible (e.g., if the number is too large), the number is stored as `#!c double`. !!! info "Notes" @@ -116,7 +116,7 @@ That is, `-0` is stored as a signed integer, but the serialization does not repr - Integer numbers are serialized as is; that is, no scientific notation is used. - Floating-point numbers are serialized as specified by the `#!c %g` printf modifier with [`std::numeric_limits::max_digits10`](https://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10) - significant digits. The rationale is to use the shortest representation while still allow round-tripping. + significant digits. The rationale is to use the shortest representation while still allowing round-tripping. !!! hint "Notes regarding precision of floating-point numbers" @@ -151,7 +151,7 @@ NaN (not-a-number) cannot be expressed with the number syntax described above an Numeric values that cannot be represented in the grammar below (such as Infinity and NaN) are not permitted. -That is, there is no way to *parse* a NaN value. However, NaN values can be stored in a JSON value by assignment. +That is, there is no way to *parse* a NaN value. However, assignments can store NaN values in a JSON value. This library serializes NaN values as `#!js null`. This corresponds to the behavior of JavaScript's [`JSON.stringify`](https://www.w3schools.com/js/js_json_stringify.asp) function. @@ -230,14 +230,14 @@ Floating-point inside JSON values numbers are compared with `#!c json::number_fl ### Number conversion Just like the C++ language itself, the `get` family of functions allows conversions between unsigned and signed -integers, and between integers and floating-point values to integers. This behavior may be surprising. +integers, and between integers and floating-point values to integers. This behavior may be surprising. !!! warning "Unconditional number conversions" ```cpp hl_lines="3" - double d = 42.3; // non-integer double value 42.3 - json jd = d; // stores double value 42.3 - std::int64_t i = jd.template get(); // now i==42; no warning or error is produced + double d = 42.3; // non-integer double value 42.3 + json jd = d; // stores double value 42.3 + std::int64_t i = jd.get(); // now i==42; no warning or error is produced ``` Note the last line with throw a [`json.exception.type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) @@ -246,7 +246,7 @@ integers, and between integers and floating-point values to integers. This beha The rationale is twofold: 1. JSON does not define a number type or precision (see above). -2. C++ also allows to silently convert between number types. +2. C++ also allows silently converting between number types. !!! success "Conditional number conversion" @@ -259,7 +259,7 @@ The rationale is twofold: if (jd.is_number_integer()) { // if so, do the conversion and use i - std::int64_t i = jd.template get(); + std::int64_t i = jd.get(); // ... } else diff --git a/docs/mkdocs/docs/home/architecture.md b/docs/mkdocs/docs/home/architecture.md new file mode 100644 index 0000000000..aba2be5803 --- /dev/null +++ b/docs/mkdocs/docs/home/architecture.md @@ -0,0 +1,124 @@ +# Architecture + +!!! info + + This page is still under construction. Its goal is to provide a high-level overview of the library's architecture. + This should help new contributors to get an idea of the used concepts and where to make changes. + +## Overview + +The main structure is class [nlohmann::basic_json](../api/basic_json/index.md). + +- public API +- container interface +- iterators + +## Template specializations + +- describe template parameters of `basic_json` +- [`json`](../api/json.md) +- [`ordered_json`](../api/ordered_json.md) via [`ordered_map`](../api/ordered_map.md) + +## Value storage + +Values are stored as a tagged union of [value_t](../api/basic_json/value_t.md) and json_value. + +```cpp +/// the type of the current element +value_t m_type = value_t::null; + +/// the value of the current element +json_value m_value = {}; +``` + +with + +```cpp +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function +}; + +union json_value { + /// object (stored with pointer to save storage) + object_t *object; + /// array (stored with pointer to save storage) + array_t *array; + /// string (stored with pointer to save storage) + string_t *string; + /// binary (stored with pointer to save storage) + binary_t *binary; + /// boolean + boolean_t boolean; + /// number (integer) + number_integer_t number_integer; + /// number (unsigned integer) + number_unsigned_t number_unsigned; + /// number (floating-point) + number_float_t number_float; +}; +``` + +## Parsing inputs (deserialization) + +Input is read via **input adapters** that abstract a source with a common interface: + +```cpp +/// read a single character +std::char_traits::int_type get_character() noexcept; + +/// read multiple characters to a destination buffer and +/// returns the number of characters successfully read +template +std::size_t get_elements(T* dest, std::size_t count = 1); +``` + +List examples of input adapters. + +## SAX Interface + +TODO + +## Writing outputs (serialization) + +Output is written via **output adapters**: + +```cpp +template +void write_character(CharType c); + +template +void write_characters(const CharType* s, std::size_t length); +``` + +List examples of output adapters. + +## Value conversion + +```cpp +template +void to_json(basic_json& j, const T& t); + +template +void from_json(const basic_json& j, T& t); +``` + +## Additional features + +- JSON Pointers +- Binary formats +- Custom base class +- Conversion macros + +## Details namespace + +- C++ feature backports diff --git a/docs/mkdocs/docs/home/code_of_conduct.md b/docs/mkdocs/docs/home/code_of_conduct.md deleted file mode 100644 index 770b8173e1..0000000000 --- a/docs/mkdocs/docs/home/code_of_conduct.md +++ /dev/null @@ -1,46 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at mail@nlohmann.me. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ diff --git a/docs/mkdocs/docs/home/customers.md b/docs/mkdocs/docs/home/customers.md new file mode 100644 index 0000000000..4bfef0c7a1 --- /dev/null +++ b/docs/mkdocs/docs/home/customers.md @@ -0,0 +1,177 @@ +# Customers + +The library is used in multiple projects, applications, operating systems, etc. The list below is not exhaustive, but +the result of an internet search. If you know further customers of the library, [please let me know](mailto:mail@nlohmann.me). + +[![](../images/customers.png)](../images/customers.png) + +## Space Exploration + +- [**Peregrine Lunar Lander Flight 01**](https://en.wikipedia.org/wiki/Peregrine_Mission_One) - The library was used for payload management in the **Peregrine Moon Lander**, developed by **Astrobotic Technology** and launched as part of NASA's **Commercial Lunar Payload Services (CLPS)** program. After six days in orbit, the spacecraft was intentionally redirected into Earth's atmosphere, where it burned up over the Pacific Ocean on **January 18, 2024**. + +## Automotive + +- [**Alexa Auto SDK**](https://github.com/alexa/alexa-auto-sdk), a software development kit enabling the integration of Alexa into automotive systems +- [**Apollo**](https://github.com/ApolloAuto/apollo), a framework for building autonomous driving systems +- [**Automotive Grade Linux (AGL)**](https://download.automotivelinux.org/AGL/release/jellyfish/latest/qemux86-64/deploy/licenses/nlohmann-json/), a collaborative open-source platform for automotive software development +- [**Genesis Motor** (infotainment)](http://webmanual.genesis.com/ccIC/AVNT/JW/KOR/English/reference010.html), a luxury automotive brand +- [**Hyundai** (infotainment)](https://www.hyundai.com/wsvc/ww/download.file.do?id=/content/hyundai/ww/data/opensource/data/GN7-2022/licenseCode/info), a global automotive brand +- [**Kia** (infotainment)](http://webmanual.kia.com/PREM_GEN6/AVNT/RJPE/KOR/Korean/reference010.html), a global automotive brand +- [**Mercedes-Benz Operating System (MB.OS)**](https://group.mercedes-benz.com/careers/about-us/mercedes-benz-operating-system/), a core component of the vehicle software ecosystem from Mercedes-Benz +- [**Rivian** (infotainment)](https://assets.ctfassets.net/2md5qhoeajym/3cwyo4eoufk4yingUwusFt/ded2c47da620fdfc99c88c7156d2c1d8/In-Vehicle_OSS_Attribution_2024__11-24_.pdf), an electric vehicle manufacturer +- [**Suzuki** (infotainment)](https://www.globalsuzuki.com/motorcycle/ipc/oss/oss_48KA_00.pdf), a global automotive and motorcycle manufacturer + +## Gaming and Entertainment + +- [**Assassin's Creed: Mirage**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a stealth-action game set in the Middle East, focusing on the journey of a young assassin with classic parkour and stealth mechanics +- [**Chasm: The Rift**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a first-person shooter blending horror and adventure, where players navigate dark realms and battle monsters +- [**College Football 25**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a college football simulation game featuring gameplay that mimics real-life college teams and competitions +- [**Concepts**](https://concepts.app/en/licenses), a digital sketching app designed for creative professionals, offering flexible drawing tools for illustration, design, and brainstorming +- [**Depthkit**](https://www.depthkit.tv/third-party-licenses), a tool for creating and capturing volumetric video, enabling immersive 3D experiences and interactive content +- [**IMG.LY**](https://img.ly/acknowledgements), a platform offering creative tools and SDKs for integrating advanced image and video editing in applications +- [**LOOT**](https://loot.readthedocs.io/_/downloads/en/0.13.0/pdf/), a tool for optimizing the load order of game plugins, commonly used in The Elder Scrolls and Fallout series +- [**Madden NFL 25**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a sports simulation game capturing the excitement of American football with realistic gameplay and team management features +- [**Marne**](https://marne.io/licenses), an unofficial private server platform for hosting custom Battlefield 1 game experiences +- [**Minecraft**](https://www.minecraft.net/zh-hant/attribution), a popular sandbox video game +- [**NHL 22**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a hockey simulation game offering realistic gameplay, team management, and various modes to enhance the hockey experience +- [**Pixelpart**](https://pixelpart.net/documentation/book/third-party.html), a 2D animation and video compositing software that allows users to create animated graphics and visual effects with a focus on simplicity and ease of use +- [**Razer Cortex**](https://mysupport.razer.com/app/answers/detail/a_id/14146/~/open-source-software-for-razer-software), a gaming performance optimizer and system booster designed to enhance the gaming experience +- [**Red Dead Redemption II**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), an open-world action-adventure game following an outlaw's story in the late 1800s, emphasizing deep storytelling and immersive gameplay +- [**Snapchat**](https://www.snap.com/terms/license-android), a multimedia messaging and augmented reality app for communication and entertainment +- [**Tactics Ogre: Reborn**](https://www.square-enix-games.com/en_US/documents/tactics-ogre-reborn-pc-installer-software-and-associated-plug-ins-disclosure), a tactical role-playing game featuring strategic battles and deep storytelling elements +- [**Throne and Liberty**](https://www.amazon.com/gp/help/customer/display.html?nodeId=T7fLNw5oAevCMtJFPj&pop-up=1), an MMORPG that offers an expansive fantasy world with dynamic gameplay and immersive storytelling +- [**Unity Vivox**](https://docs.unity3d.com/Packages/com.unity.services.vivox@15.1/license/Third%20Party%20Notices.html), a communication service that enables voice and text chat functionality in multiplayer games developed with Unity +- [**Zool: Redimensioned**](https://www.mobygames.com/person/1195889/niels-lohmann/credits/), a modern reimagining of the classic platformer featuring fast-paced gameplay and vibrant environments +- [**immersivetech**](https://immersitech.io/open-source-third-party-software/), a technology company focused on immersive experiences, providing tools and solutions for virtual and augmented reality applications + +## Consumer Electronics + +- [**Audinate**](https://www.audinate.com/legal/software-licensing/dante-av-h-open-source-licenses/), a provider of networked audio solutions specializing in Dante technology, which facilitates high-quality digital audio transport over IP networks +- [**Canon CanoScan LIDE**](https://carolburo.com/wp-content/uploads/2024/06/LiDE400_OnlineManual_Win_FR_V02.pdf), a series of flatbed scanners offering high-resolution image scanning for home and office use +- [**Canon PIXMA Printers**](https://www.mediaexpert.pl/products/files/73/7338196/Instrukcja-obslugi-CANON-Pixma-TS7450i.pdf), a line of all-in-one inkjet printers known for high-quality printing and wireless connectivity +- [**Cisco Webex Desk Camera**](https://www.cisco.com/c/dam/en_us/about/doing_business/open_source/docs/CiscoWebexDeskCamera-23-1622100417.pdf), a video camera designed for professional-quality video conferencing and remote collaboration +- [**Philips Hue Personal Wireless Lighting**](http://2ak5ape.257.cz/), a smart lighting system for customizable and wireless home illumination +- [**Ray-Ban Meta Smart glasses**](https://www.meta.com/de/en/legal/smart-glasses/third-party-notices-android/03/), a pair of smart glasses designed for capturing photos and videos with integrated connectivity and social features +- [**Razer Synapse**](https://mysupport.razer.com/app/answers/detail/a_id/14146/~/open-source-software-for-razer-software), a unified configuration software enabling hardware customization for Razer devices +- [**Siemens SINEMA Remote Connect**](https://cache.industry.siemens.com/dl/files/790/109793790/att_1054961/v2/OSS_SINEMA-RC_86.pdf), a remote connectivity solution for monitoring and managing industrial networks and devices securely +- [**Sony PlayStation 4**](https://doc.dl.playstation.net/doc/ps4-oss/index.html), a gaming console developed by Sony that offers a wide range of games and multimedia entertainment features +- [**Sony Virtual Webcam Driver for Remote Camera**](https://helpguide.sony.net/rc/vwd/v1/zh-cn/print.pdf), a software driver that enables the use of Sony cameras as virtual webcams for video conferencing and streaming + +## Operating Systems + +- [**Apple iOS and macOS**](https://www.apple.com/macos), a family of operating systems developed by Apple, including iOS for mobile devices and macOS for desktop computers +- [**Google Fuchsia**](https://fuchsia.googlesource.com/third_party/json/), an open-source operating system developed by Google, designed to be secure, updatable, and adaptable across various devices +- [**SerenityOS**](https://github.com/SerenityOS/serenity), an open-source operating system that aims to provide a simple and beautiful user experience with a focus on simplicity and elegance +- [**Yocto**](http://ftp.emacinc.com/openembedded-sw/kirkstone-icop-5.15-kirkstone-6.0/archive-2024-10/pn8m-090t-ppc/licenses/nlohmann-json/), a Linux-based build system for creating custom operating systems and software distributions, tailored for embedded devices and IoT applications + +## Development Tools and IDEs + +- [**Accentize SpectralBalance**](https://www.accentize.com/products/SpectralBalanceManual.pdf), an adaptive speech analysis tool designed to enhance audio quality by optimizing frequency balance in recordings +- [**Arm Compiler for Linux**](https://documentation-service.arm.com/static/66558e9d876c8d213b7843e4), a software development toolchain for compiling and optimizing applications on Arm-based Linux systems +- [**BBEdit**](https://s3.amazonaws.com/BBSW-download/BBEdit_15.1.2_User_Manual.pdf), a professional text and code editor for macOS +- [**CoderPad**](https://coderpad.io), a collaborative coding platform that enables real-time code interviews and assessments for developers; the library is included in every CoderPad instance and can be accessed with a simple `#include "json.hpp"` +- [**Compiler Explorer**](https://godbolt.org), a web-based tool that allows users to write, compile, and visualize the assembly output of code in various programming languages; the library is readily available and accessible with the directive `#include `. +- [**GitHub CodeQL**](https://github.com/github/codeql), a code analysis tool used for identifying security vulnerabilities and bugs in software through semantic queries +- [**Hex-Rays**](https://docs.hex-rays.com/user-guide/user-interface/licenses), a reverse engineering toolset for analyzing and decompiling binaries, primarily used for security research and vulnerability analysis +- [**ImHex**](https://github.com/WerWolv/ImHex), a hex editor designed for reverse engineering, providing advanced features for data analysis and manipulation +- [**Intel GPA Framework**](https://intel.github.io/gpasdk-doc/src/licenses.html), a suite of cross-platform tools for capturing, analyzing, and optimizing graphics applications across different APIs +- [**Intopix**](https://www.intopix.com/software-licensing), a provider of advanced image processing and compression solutions used in software development and AV workflows +- [**MKVToolNix**](https://mkvtoolnix.download/doc/README.md), a set of tools for creating, editing, and inspecting MKV (Matroska) multimedia container files +- [**Meta Yoga**](https://github.com/facebook/yoga), a layout engine that facilitates flexible and efficient user interface design across multiple platforms +- [**NVIDIA Nsight Compute**](https://docs.nvidia.com/nsight-compute/2022.2/pdf/CopyrightAndLicenses.pdf), a performance analysis tool for CUDA applications that provides detailed insights into GPU performance metrics +- [**Notepad++**](https://github.com/notepad-plus-plus/notepad-plus-plus), a free source code editor that supports various programming languages +- [**OpenRGB**](https://gitlab.com/CalcProgrammer1/OpenRGB), an open source RGB lighting control that doesn't depend on manufacturer software +- [**OpenTelemetry C++**](https://github.com/open-telemetry/opentelemetry-cpp), a library for collecting and exporting observability data in C++, enabling developers to implement distributed tracing and metrics in their application +- [**Qt Creator**](https://doc.qt.io/qtcreator/qtcreator-attribution-json-nlohmann.html), an IDE for developing applications using the Qt application framework +- [**Scanbot SDK**](https://docs.scanbot.io/barcode-scanner-sdk/web/third-party-libraries/), a software development kit (SDK) that provides tools for integrating advanced document scanning and barcode scanning capabilities into applications + +## Machine Learning and AI + +- [**Apple Core ML Tools**](https://github.com/apple/coremltools), a set of tools for converting and configuring machine learning models for deployment in Apple's Core ML framework +- [**Avular Mobile Robotics**](https://www.avular.com/licenses/nlohmann-json-3.9.1.txt), a platform for developing and deploying mobile robotics solutions +- [**Google gemma.cpp**](https://github.com/google/gemma.cpp), a lightweight C++ inference engine designed for running AI models from the Gemma family +- [**llama.cpp**](https://github.com/ggerganov/llama.cpp), a C++ library designed for efficient inference of large language models (LLMs), enabling streamlined integration into applications +- [**MLX**](https://github.com/ml-explore/mlx), an array framework for machine learning on Apple Silicon +- [**Mozilla llamafile**](https://github.com/Mozilla-Ocho/llamafile), a tool designed for distributing and executing large language models (LLMs) efficiently using a single file format +- [**NVIDIA ACE**](https://docs.nvidia.com/ace/latest/index.html), a suite of real-time AI solutions designed for the development of interactive avatars and digital human applications, enabling scalable and sophisticated user interactions +- [**Peer**](https://support.peer.inc/hc/en-us/articles/17261335054235-Licenses), a platform offering personalized AI assistants for interactive learning and creative collaboration +- [**stable-diffusion.cpp**](https://github.com/leejet/stable-diffusion.cpp), a C++ implementation of the Stable Diffusion image generation model +- [**TanvasTouch**](https://tanvas.co/tanvastouch-sdk-third-party-acknowledgments), a software development kit (SDK) that enables developers to create tactile experiences on touchscreens, allowing users to feel textures and physical sensations in a digital environment +- [**TensorFlow**](https://github.com/tensorflow/tensorflow), a machine learning framework that facilitates the development and training of models, supporting data serialization and efficient data exchange between components + +## Scientific Research and Analysis + +- [**BLACK**](https://www.black-sat.org/en/stable/installation/linux.html), a bounded linear temporal logic (LTL) satisfiability checker +- [**CERN Atlas Athena**](https://gitlab.cern.ch/atlas/athena/-/blob/main/Control/PerformanceMonitoring/PerfMonComps/src/PerfMonMTSvc.h), a software framework used in the ATLAS experiment at the Large Hadron Collider (LHC) for performance monitoring +- [**ICU**](https://github.com/unicode-org/icu), the International Components for Unicode, a mature library for software globalization and multilingual support +- [**KAMERA**](https://github.com/Kitware/kamera), a platform for synchronized data collection and real-time deep learning to map marine species like polar bears and seals, aiding Arctic ecosystem research +- [**KiCad**](https://gitlab.com/kicad/code/kicad/-/tree/master/thirdparty/nlohmann_json), a free and open-source software suite for electronic design automation +- [**Maple**](https://www.maplesoft.com/support/help/Maple/view.aspx?path=copyright), a symbolic and numeric computing environment for advanced mathematical modeling and analysis +- [**MeVisLab**](https://mevislabdownloads.mevis.de/docs/current/MeVis/ThirdParty/Documentation/Publish/ThirdPartyReference/index.html), a software framework for medical image processing and visualization. +- [**OpenPMD API**](https://openpmd-api.readthedocs.io/en/0.8.0-alpha/backends/json.html), a versatile programming interface for accessing and managing scientific data, designed to facilitate the efficient storage, retrieval, and sharing of simulation data across various applications and platforms +- [**ParaView**](https://github.com/Kitware/ParaView), an open-source tool for large-scale data visualization and analysis across various scientific domains +- [**QGIS**](https://gitlab.b-data.ch/qgis/qgis/-/blob/backport-57658-to-release-3_34/external/nlohmann/json.hpp), a free and open-source geographic information system (GIS) application that allows users to create, edit, visualize, and analyze geospatial data across a variety of formats +- [**VTK**](https://github.com/Kitware/VTK), a software library for 3D computer graphics, image processing, and visualization +- [**VolView**](https://github.com/Kitware/VolView), a lightweight application for interactive visualization and analysis of 3D medical imaging data. + +## Business and Productivity Software + +- [**ArcGIS PRO**](https://www.esri.com/content/dam/esrisites/en-us/media/legal/open-source-acknowledgements/arcgis-pro-2-8-attribution-report.html), a desktop geographic information system (GIS) application developed by Esri for mapping and spatial analysis +- [**Autodesk Desktop**](https://damassets.autodesk.net/content/dam/autodesk/www/Company/legal-notices-trademarks/autodesk-desktop-platform-components/internal-autodesk-components-web-page-2023.pdf), a software platform developed by Autodesk for creating and managing desktop applications and services +- [**Check Point**](https://www.checkpoint.com/about-us/copyright-and-trademarks/), a cybersecurity company specializing in threat prevention and network security solutions, offering a range of products designed to protect enterprises from cyber threats and ensure data integrity +- [**Microsoft Office for Mac**](https://officecdnmac.microsoft.com/pr/legal/mac/OfficeforMacAttributions.html), a suite of productivity applications developed by Microsoft for macOS, including tools for word processing, spreadsheets, and presentations +- [**Microsoft Teams**](https://www.microsoft.com/microsoft-teams/), a team collaboration application offering workspace chat and video conferencing, file storage, and integration of proprietary and third-party applications and services +- [**Nexthink Infinity**](https://docs.nexthink.com/legal/services-terms/experience-open-source-software-licenses/infinity-2022.8-software-licenses), a digital employee experience management platform for monitoring and improving IT performance +- [**Sophos Connect Client**](https://docs.sophos.com/nsg/licenses/SophosConnect/SophosConnectAttribution.html), a secure VPN client from Sophos that allows remote users to connect to their corporate network, ensuring secure access to resources and data +- [**Stonebranch**](https://stonebranchdocs.atlassian.net/wiki/spaces/UA77/pages/799545647/Licenses+for+Third-Party+Libraries), a cloud-based cybersecurity solution that integrates backup, disaster recovery, and cybersecurity features to protect data and ensure business continuity for organizations +- [**Tablecruncher**](https://tablecruncher.com/), a data analysis tool that allows users to import, analyze, and visualize spreadsheet data, offering interactive features for better insights and decision-making +- [**magicplan**](https://help.magicplan.app/acknowledgments), a mobile application for creating floor plans and interior designs using augmented reality + +## Databases and Big Data + +- [**ADIOS2**](https://code.ornl.gov/ecpcitest/adios2/-/tree/pr4285_FFSUpstream/thirdparty/nlohmann_json?ref_type=heads), a data management framework designed for high-performance input and output operations +- [**Cribl Stream**](https://docs.cribl.io/stream/third-party-current-list/), a real-time data processing platform that enables organizations to collect, route, and transform observability data, enhancing visibility and insights into their systems +- [**DB Browser for SQLite**](https://github.com/sqlitebrowser/sqlitebrowser), a visual open-source tool for creating, designing, and editing SQLite database files +- [**MySQL Connector/C++**](https://docs.oracle.com/cd/E17952_01/connector-cpp-9.1-license-com-en/license-opentelemetry-cpp-com.html), a C++ library for connecting and interacting with MySQL databases +- [**MySQL NDB Cluster**](https://downloads.mysql.com/docs/licenses/cluster-9.0-com-en.pdf), a distributed database system that provides high availability and scalability for MySQL databases +- [**MySQL Shell**](https://downloads.mysql.com/docs/licenses/mysql-shell-8.0-gpl-en.pdf), an advanced client and code editor for interacting with MySQL servers, supporting SQL, Python, and JavaScript +- [**PrestoDB**](https://github.com/prestodb/presto), a distributed SQL query engine designed for large-scale data analytics, originally developed by Facebook +- [**ROOT Data Analysis Framework**](https://root.cern/doc/v614/classnlohmann_1_1basic__json.html), an open-source data analysis framework widely used in high-energy physics and other fields for data processing and visualization +- [**WiredTiger**](https://github.com/wiredtiger/wiredtiger), a high-performance storage engine for databases, offering support for compression, concurrency, and checkpointing + +## Simulation and Modeling + +- [**Arcturus HoloSuite**](https://www.datocms-assets.com/104353/1698904597-holosuite-third-party-software-credits-and-attributions-2.pdf), a software toolset for capturing, editing, and streaming volumetric video, featuring advanced compression technologies for high-quality 3D content creation +- [**azul**](https://pure.tudelft.nl/ws/files/85338589/tgis.12673.pdf), a fast and efficient 3D city model viewer designed for visualizing urban environments and spatial data +- [**Blender**](https://projects.blender.org/blender/blender/search?q=nlohmann), a free and open-source 3D creation suite for modeling, animation, rendering, and more +- [**cpplot**](https://cpplot.readthedocs.io/en/latest/library_api/function_eigen_8h_1ac080eac0541014c5892a55e41bf785e6.html), a library for creating interactive graphs and charts in C++, which can be viewed in web browsers +- [**Foundry Nuke**](https://learn.foundry.com/nuke/content/misc/studio_third_party_libraries.html), a powerful node-based digital compositing and visual effects application used in film and television post-production +- [**GAMS**](https://www.gams.com/47/docs/THIRDPARTY.html), a high-performance mathematical modeling system for optimization and decision support +- [**Kitware SMTK**](https://github.com/Kitware/SMTK), a software toolkit for managing simulation models and workflows in scientific and engineering applications +- [**M-Star**](https://docs.mstarcfd.com/3_Licensing/thirdparty-licenses.html), a computational fluid dynamics software for simulating and analyzing fluid flow +- [**MapleSim CAD Toolbox**](https://www.maplesoft.com/support/help/MapleSim/view.aspx?path=CADToolbox/copyright), a software extension for MapleSim that integrates CAD models, allowing users to import, manipulate, and analyze 3D CAD data within the MapleSim environment for enhanced modeling and simulation +- [**NVIDIA Omniverse**](https://docs.omniverse.nvidia.com/composer/latest/common/product-licenses/usd-explorer/usd-explorer-2023.2.0-licenses-manifest.html), a platform for 3D content creation and collaboration that enables real-time simulations and interactive experiences across various industries +- [**Pixar Renderman**](https://rmanwiki-26.pixar.com/space/REN26/19662083/Legal+Notice), a photorealistic 3D rendering software developed by Pixar, widely used in the film industry for creating high-quality visual effects and animations +- [**ROS - Robot Operating System**](http://docs.ros.org/en/noetic/api/behaviortree_cpp/html/json_8hpp_source.html), a set of software libraries and tools that assist in developing robot applications +- [**UBS**](https://www.ubs.com/), a multinational financial services and banking company + +## Enterprise and Cloud Applications + +- [**Acronis Cyber Protect Cloud**](https://care.acronis.com/s/article/59533-Third-party-software-used-in-Acronis-Cyber-Protect-Cloud?language=en_US), an all-in-one data protection solution that combines backup, disaster recovery, and cybersecurity to safeguard business data from threats like ransomware +- [**Baereos**](https://gitlab.tiger-computing.co.uk/packages/bareos/-/blob/tiger/bullseye/third-party/CLI11/examples/json.cpp), a backup solution that provides data protection and recovery options for various environments, including physical and virtual systems +- [**Bitdefender Home Scanner**](https://www.bitdefender.de/site/Main/view/home-scanner-open-source.html), a tool from Bitdefender that scans devices for malware and security threats, providing a safeguard against potential online dangers +- [**Citrix Provisioning**](https://docs.citrix.com/en-us/provisioning/2203-ltsr/downloads/pvs-third-party-notices-2203.pdf), a solution that streamlines the delivery of virtual desktops and applications by allowing administrators to manage and provision resources efficiently across multiple environments +- [**Citrix Virtual Apps and Desktops**](https://docs.citrix.com/en-us/citrix-virtual-apps-desktops/2305/downloads/third-party-notices-apps-and-desktops.pdf), a solution from Citrix that delivers virtual apps and desktops +- [**Cyberarc**](https://docs.cyberark.com/Downloads/Legal/Privileged%20Session%20Manager%20for%20SSH%20Third-Party%20Notices.pdf), a security solution that specializes in privileged access management, enabling organizations to control and monitor access to critical systems and data, thereby enhancing overall cybersecurity posture +- [**Egnyte Desktop**](https://helpdesk.egnyte.com/hc/en-us/articles/360007071732-Third-Party-Software-Acknowledgements), a secure cloud storage solution designed for businesses, enabling file sharing, collaboration, and data management across teams while ensuring compliance and data protection +- [**Elster**](https://www.secunet.com/en/about-us/press/article/elstersecure-bietet-komfortablen-login-ohne-passwort-dank-secunet-protect4use), a digital platform developed by German tax authorities for secure and efficient electronic tax filing and management using secunet protect4use +- [**Ethereum Solidity**](https://github.com/ethereum/solidity), a high-level, object-oriented programming language designed for implementing smart contracts on the Ethereum platform +- [**Inciga**](https://fossies.org/linux/icinga2/third-party/nlohmann_json/json.hpp), a monitoring tool for IT infrastructure, designed to provide insights into system performance and availability through customizable dashboards and alerts +- [**Intel Accelerator Management Daemon for VMware ESXi**](https://downloadmirror.intel.com/772507/THIRD-PARTY.txt), a management tool designed for monitoring and controlling Intel hardware accelerators within VMware ESXi environments, optimizing performance and resource allocation +- [**Juniper Identity Management Service**](https://www.juniper.net/documentation/us/en/software/jims/jims-guide/jims-guide.pdf) +- [**Microsoft Azure IoT SDK**](https://library.e.abb.com/public/2779c5f85f30484192eb3cb3f666a201/IP%20Gateway%20Open%20License%20Declaration_9AKK108467A4095_Rev_C.pdf), a collection of tools and libraries to help developers connect, build, and deploy Internet of Things (IoT) solutions on the Azure cloud platform +- [**Microsoft WinGet**](https://github.com/microsoft/winget-cli), a command-line utility included in the Windows Package Manager +- [**plexusAV**](https://www.sisme.com/media/10994/manual_plexusav-p-avn-4-form8244-c.pdf), a high-performance AV-over-IP transceiver device capable of video encoding and decoding using the IPMX standard +- [**Pointr**](https://docs-dev.pointr.tech/docs/8.x/Developer%20Portal/Open%20Source%20Licenses/), a platform for indoor positioning and navigation solutions, offering tools and SDKs for developers to create location-based applications +- [**secunet protect4use**](https://www.secunet.com/en/about-us/press/article/elstersecure-bietet-komfortablen-login-ohne-passwort-dank-secunet-protect4use), a secure, passwordless multifactor authentication solution that transforms smartphones into digital keyrings, ensuring high security for online services and digital identities +- [**Sencore MRD 7000**](https://www.foccusdigital.com/wp-content/uploads/2025/03/MRD-7000-Manual-8175V.pdf), a professional multi-channel receiver and decoder supporting UHD and HD stream decoding diff --git a/docs/mkdocs/docs/home/design_goals.md b/docs/mkdocs/docs/home/design_goals.md index b80551fe97..0a0f77029f 100644 --- a/docs/mkdocs/docs/home/design_goals.md +++ b/docs/mkdocs/docs/home/design_goals.md @@ -2,11 +2,11 @@ There are myriads of [JSON](https://json.org) libraries out there, and each may even have its reason to exist. Our class had these design goals: -- **Intuitive syntax**. In languages such as Python, JSON feels like a first class data type. We used all the operator magic of modern C++ to achieve the same feeling in your code. Check out the [examples below](#examples), and you'll know what I mean. +- **Intuitive syntax**. In languages such as Python, JSON feels like a first-class data type. We used all the operator magic of modern C++ to achieve the same feeling in your code. - **Trivial integration**. Our whole code consists of a single header file [`json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp). That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings. -- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/tree/develop/test/src) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](http://valgrind.org) and the [Clang Sanitizers](https://clang.llvm.org/docs/index.html) that there are no memory leaks. [Google OSS-Fuzz](https://github.com/google/oss-fuzz/tree/master/projects/json) additionally runs fuzz tests against all parsers 24/7, effectively executing billions of tests so far. To maintain high quality, the project is following the [Core Infrastructure Initiative (CII) best practices](https://bestpractices.coreinfrastructure.org/projects/289). +- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/tree/develop/tests/src) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](http://valgrind.org) and the [Clang Sanitizers](https://clang.llvm.org/docs/index.html) that there are no memory leaks. [Google OSS-Fuzz](https://github.com/google/oss-fuzz/tree/master/projects/json) additionally runs fuzz tests against all parsers 24/7, effectively executing billions of tests so far. To maintain high quality, the project is following the [Core Infrastructure Initiative (CII) best practices](https://bestpractices.coreinfrastructure.org/projects/289). Other aspects were not so important to us: diff --git a/docs/mkdocs/docs/home/exceptions.md b/docs/mkdocs/docs/home/exceptions.md index a0fee9e333..30487348ee 100644 --- a/docs/mkdocs/docs/home/exceptions.md +++ b/docs/mkdocs/docs/home/exceptions.md @@ -6,24 +6,33 @@ All exceptions inherit from class `json::exception` (which in turn inherits from `std::exception`). It is used as the base class for all exceptions thrown by the `basic_json` class. This class can hence be used as "wildcard" to catch exceptions. -```plantuml -std::exception <|-- json::exception -json::exception <|-- json::parse_error -json::exception <|-- json::invalid_iterator -json::exception <|-- json::type_error -json::exception <|-- json::out_of_range -json::exception <|-- json::other_error - -interface std::exception {} - -class json::exception { - + const int id - + const char* what() const -} - -class json::parse_error { - + const std::size_t byte -} +``` mermaid +classDiagram + direction LR + class `std::exception` { + <> + } + + class `json::exception` { + +const int id + +const char* what() const + } + + class `json::parse_error` { + +const std::size_t byte + } + + class `json::invalid_iterator` + class `json::type_error` + class `json::out_of_range` + class `json::other_error` + + `std::exception` <|-- `json::exception` + `json::exception` <|-- `json::parse_error` + `json::exception` <|-- `json::invalid_iterator` + `json::exception` <|-- `json::type_error` + `json::exception` <|-- `json::out_of_range` + `json::exception` <|-- `json::other_error` ``` ### Switch off exceptions @@ -72,7 +81,7 @@ Exceptions in the library are thrown in the local context of the JSON value they This exception can be hard to debug if storing the value `#!c "12"` and accessing it is further apart. -To create better diagnostics messages, each JSON value needs a pointer to its parent value such that a global context (i.e., a path from the root value to the value that lead to the exception) can be created. That global context is provided as [JSON Pointer](../features/json_pointer.md). +To create better diagnostics messages, each JSON value needs a pointer to its parent value such that a global context (i.e., a path from the root value to the value that led to the exception) can be created. That global context is provided as [JSON Pointer](../features/json_pointer.md). As this global context comes at the price of storing one additional pointer per JSON value and runtime overhead to maintain the parent relation, extended diagnostics are disabled by default. They can, however, be enabled by defining the preprocessor symbol [`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md) to `1` before including `json.hpp`. @@ -94,7 +103,7 @@ See [documentation of `JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md) for ## Parse errors -This exception is thrown by the library when a parse error occurs. Parse errors +The library throws this exception when a parse error occurs. Parse errors can occur during the deserialization of JSON text, CBOR, MessagePack, as well as when using JSON Patch. @@ -471,7 +480,7 @@ The offset operators (`+`, `-`, `+=`, `-=`) cannot be used on iterators belongin ### json.exception.invalid_iterator.210 -The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (`first`, `last`) is invalid. +The iterator range passed to the insert function is not compatible, meaning they do not belong to the same container. Therefore, the range (`first`, `last`) is invalid. !!! failure "Example message" @@ -511,7 +520,7 @@ The order of object iterators cannot be compared, because JSON objects are unord ### json.exception.invalid_iterator.214 -Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to `begin()`. +Cannot retrieve value from iterator: The iterator either refers to a null value, or it refers to a primitive type (number, boolean, or string), but does not match the iterator returned by `begin()`. !!! failure "Example message" @@ -551,7 +560,7 @@ To create an object from an initializer list, the initializer list must consist ### json.exception.type_error.302 -During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. +During implicit or explicit value conversion, the JSON type must be compatible with the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. !!! failure "Example messages" @@ -731,7 +740,7 @@ The `dump()` function only works with UTF-8 encoded strings; that is, if you ass ### json.exception.type_error.317 -The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) +The dynamic type of the object cannot be represented in the requested serialization format (e.g., a raw `true` or `null` JSON object cannot be serialized to BSON) !!! failure "Example messages" @@ -750,7 +759,7 @@ The dynamic type of the object cannot be represented in the requested serializat ## Out of range -This exception is thrown in case a library function is called on an input parameter that exceeds the expected range, for instance in case of array indices or nonexisting object keys. +This exception is thrown in case a library function is called on an input parameter that exceeds the expected range, for instance, in the case of array indices or nonexisting object keys. Exceptions have ids 4xx. @@ -810,7 +819,7 @@ A reference token in a JSON Pointer could not be resolved. ### json.exception.out_of_range.405 -The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. +The JSON Patch operations 'remove' and 'add' cannot be applied to the root element of the JSON value. !!! failure "Example message" @@ -830,17 +839,14 @@ A parsed number could not be stored as without changing it to NaN or INF. ### json.exception.out_of_range.407 -UBJSON and BSON only support integer numbers up to 9223372036854775807. +This exception previously indicated that the UBJSON and BSON binary formats did not support integer numbers greater than +9223372036854775807 due to limitations in the implemented mapping. However, these limitations have since been resolved, +and this exception no longer occurs. -!!! failure "Example message" +!!! success "Exception cannot occur any more" - ``` - number overflow serializing '9223372036854775808' - ``` - -!!! note - - Since version 3.9.0, integer numbers beyond int64 are serialized as high-precision UBJSON numbers, and this exception does not further occur. + - Since version 3.9.0, integer numbers beyond int64 are serialized as high-precision UBJSON numbers. + - Since version 3.12.0, integer numbers beyond int64 are serialized as uint64 BSON numbers. ### json.exception.out_of_range.408 @@ -894,3 +900,13 @@ A JSON Patch operation 'test' failed. The unsuccessful operation is also printed ``` [json.exception.other_error.501] unsuccessful: {"op":"test","path":"/baz","value":"bar"} ``` + +### json.exception.other_error.502 + +This exception is thrown when a null pointer is passed as SAX event listener to the [sax_parse](../api/basic_json/sax_parse.md) function. + +!!! failure "Example message" + + ``` + [json.exception.other_error.502] SAX handler must not be null + ``` diff --git a/docs/mkdocs/docs/home/faq.md b/docs/mkdocs/docs/home/faq.md index dd426e073f..74709bac68 100644 --- a/docs/mkdocs/docs/home/faq.md +++ b/docs/mkdocs/docs/home/faq.md @@ -46,7 +46,7 @@ for objects. Can you add an option to ignore trailing commas? -This library does not support any feature which would jeopardize interoperability. +This library does not support any feature that would jeopardize interoperability. ### Parse errors reading non-ASCII characters @@ -59,15 +59,15 @@ This library does not support any feature which would jeopardize interoperabilit The library supports **Unicode input** as follows: -- Only **UTF-8** encoded input is supported which is the default encoding for JSON according to [RFC 8259](https://tools.ietf.org/html/rfc8259.html#section-8.1). +- Only **UTF-8** encoded input is supported, which is the default encoding for JSON, according to [RFC 8259](https://tools.ietf.org/html/rfc8259.html#section-8.1). - `std::u16string` and `std::u32string` can be parsed, assuming UTF-16 and UTF-32 encoding, respectively. These encodings are not supported when reading from files or other input containers. - Other encodings such as Latin-1 or ISO 8859-1 are **not** supported and will yield parse or serialization errors. -- [Unicode noncharacters](http://www.unicode.org/faq/private_use.html#nonchar1) will not be replaced by the library. +- 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. - 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. -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. ### Wide string handling @@ -177,4 +177,4 @@ The code compiles successfully with [Android NDK](https://developer.android.com/ - Why do I get a compilation error `'to_string' is not a member of 'std'` (or similarly, for `strtod` or `strtof`)? - Why does the code not compile with MinGW or Android SDK? -This is not an issue with the code, but rather with the compiler itself. On Android, see above to build with a newer environment. For MinGW, please refer to [this site](http://tehsausage.com/mingw-to-string) and [this discussion](https://github.com/nlohmann/json/issues/136) for information on how to fix this bug. For Android NDK using `APP_STL := gnustl_static`, please refer to [this discussion](https://github.com/nlohmann/json/issues/219). +This is not an issue with the code, but rather with the compiler itself. On Android, see above to build with a newer environment. For MinGW, please refer to [this site](http://tehsausage.com/mingw-to-string) and [this discussion](https://github.com/nlohmann/json/issues/136) for information on how to fix this bug. For Android NDK using `APP_STL := gnustl_static`, please refer to [this discussion](https://github.com/nlohmann/json/issues/219). diff --git a/docs/mkdocs/docs/home/license.md b/docs/mkdocs/docs/home/license.md index baef2f51b9..597c694961 100644 --- a/docs/mkdocs/docs/home/license.md +++ b/docs/mkdocs/docs/home/license.md @@ -1,10 +1,10 @@ # License - + The class is licensed under the [MIT License](https://opensource.org/licenses/MIT): -Copyright © 2013-2022 [Niels Lohmann](https://nlohmann.me) +Copyright © 2013-2026 [Niels Lohmann](https://nlohmann.me) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/docs/mkdocs/docs/home/releases.md b/docs/mkdocs/docs/home/releases.md index 5237c42592..48230c229c 100644 --- a/docs/mkdocs/docs/home/releases.md +++ b/docs/mkdocs/docs/home/releases.md @@ -251,7 +251,7 @@ http://nlohmann.github.io/json/doxygen/classnlohmann_1_1basic__json_a0a45fc74063 - Fixed typos and broken links in README. #1417 #1423 #1425 #1451 #1455 #1491 - Fixed documentation of parse function. #1473 - Suppressed warning that cannot be fixed inside the library. #1401 #1468 -- Imroved package manager suppert: +- Improved package manager suppert: - Updated Buckaroo instructions. #1495 - Improved Meson support. #1463 - Added Conda package manager documentation. #1430 @@ -596,7 +596,7 @@ This release adds support for the [**UBJSON**](http://ubjson.org) format and [** ### :sparkles: New features -- The library now supports [**UBJSON**](http://ubjson.org) (Universal Binary JSON Specification) as binary format to read and write JSON values space-efficiently. See the [documentation overview](https://github.com/nlohmann/json/blob/develop/doc/binary_formats.md) for a comparison of the different formats CBOR, MessagePack, and UBJSON. +- The library now supports [**UBJSON**](http://ubjson.org) (Universal Binary JSON Specification) as binary format to read and write JSON values space-efficiently. See the [documentation overview](https://json.nlohmann.me/features/binary_formats/) for a comparison of the different formats CBOR, MessagePack, and UBJSON. - [**JSON Merge Patch**](https://tools.ietf.org/html/rfc7386) (RFC 7386) offers an intuitive means to describe patches between JSON values (#876, #877). See the documentation of [`merge_patch`](http://nlohmann.github.io/json/doxygen/classnlohmann_1_1basic__json_a0ec0cd19cce42ae6071f3cc6870ea295.html#a0ec0cd19cce42ae6071f3cc6870ea295) for more information. ### :zap: Improvements @@ -685,10 +685,10 @@ After almost a year, here is finally a new release of JSON for Modern C++, and i This section describes changes that change the public API of the library and may require changes in code using a previous version of the library. In section "Moving from 2.x.x to 3.0.0" at the end of the release notes, we describe in detail how existing code needs to be changed. -- The library now uses [**user-defined exceptions**](http://nlohmann.github.io/json/doxygen/classnlohmann_1_1basic__json_a9a0aced019cb1d65bb49703406c84970.html#a9a0aced019cb1d65bb49703406c84970) instead of re-using those defined in `` (#244). This not only allows to add more information to the exceptions (every exception now has an identifier, and parse errors contain the position of the error), but also to easily catch all library exceptions with a single `catch(json::exception)`. +- The library now uses [**user-defined exceptions**](http://nlohmann.github.io/json/doxygen/classnlohmann_1_1basic__json_a9a0aced019cb1d65bb49703406c84970.html#a9a0aced019cb1d65bb49703406c84970) instead of reusing those defined in `` (#244). This not only allows to add more information to the exceptions (every exception now has an identifier, and parse errors contain the position of the error), but also to easily catch all library exceptions with a single `catch(json::exception)`. - When strings with a different encoding as UTF-8 were stored in JSON values, their serialization could not be parsed by the library itself, as only UTF-8 is supported. To enforce this library limitation and improve consistency, **non-UTF-8 encoded strings now yield a `json::type_error` exception during serialization** (#838). The check for valid UTF-8 is realized with code from [Björn Hoehrmann](http://bjoern.hoehrmann.de/). - **NaN and infinity values can now be stored inside the JSON value** without throwing an exception. They are, however, still serialized as `null` (#388). -- The library's iterator tag was changed from RandomAccessIterator to **[BidirectionalIterator](http://en.cppreference.com/w/cpp/concept/BidirectionalIterator)** (#593). Supporting RandomAccessIterator was incorrect as it assumed an ordering of values in a JSON objects which are unordered by definition. +- The library's iterator tag was changed from RandomAccessIterator to **[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator)** (#593). Supporting RandomAccessIterator was incorrect as it assumed an ordering of values in a JSON objects which are unordered by definition. - The library does not include the standard headers ``, ``, and `` any more. You may need to add these headers to code relying on them. - Removed constructor `explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr)` which was deprecated in version 2.0.0 (#480). @@ -744,54 +744,54 @@ There are five different exceptions inheriting from [`json::exception`](http://n To support these exception, the `try`/`catch` blocks of your code need to be adjusted: -| new exception | previous exception | -|:--|:--| -| parse_error.101 | invalid_argument | -| parse_error.102 | invalid_argument | -| parse_error.103 | invalid_argument | -| parse_error.104 | invalid_argument | -| parse_error.105 | invalid_argument | -| parse_error.106 | domain_error | -| parse_error.107 | domain_error | -| parse_error.108 | domain_error | -| parse_error.109 | invalid_argument | -| parse_error.110 | out_of_range | -| parse_error.111 | invalid_argument | -| parse_error.112 | invalid_argument | -| invalid_iterator.201 | domain_error | -| invalid_iterator.202 | domain_error | -| invalid_iterator.203 | domain_error | -| invalid_iterator.204 | out_of_range | -| invalid_iterator.205 | out_of_range | -| invalid_iterator.206 | domain_error | -| invalid_iterator.207 | domain_error | -| invalid_iterator.208 | domain_error | -| invalid_iterator.209 | domain_error | -| invalid_iterator.210 | domain_error | -| invalid_iterator.211 | domain_error | -| invalid_iterator.212 | domain_error | -| invalid_iterator.213 | domain_error | -| invalid_iterator.214 | out_of_range | -| type_error.301 | domain_error | -| type_error.302 | domain_error | -| type_error.303 | domain_error | -| type_error.304 | domain_error | -| type_error.305 | domain_error | -| type_error.306 | domain_error | -| type_error.307 | domain_error | -| type_error.308 | domain_error | -| type_error.309 | domain_error | -| type_error.310 | domain_error | -| type_error.311 | domain_error | -| type_error.313 | domain_error | -| type_error.314 | domain_error | -| type_error.315 | domain_error | -| out_of_range.401 | out_of_range | -| out_of_range.402 | out_of_range | -| out_of_range.403 | out_of_range | -| out_of_range.404 | out_of_range | -| out_of_range.405 | domain_error | -| other_error.501 | domain_error | +| new exception | previous exception | +|:---------------------|:-------------------| +| parse_error.101 | invalid_argument | +| parse_error.102 | invalid_argument | +| parse_error.103 | invalid_argument | +| parse_error.104 | invalid_argument | +| parse_error.105 | invalid_argument | +| parse_error.106 | domain_error | +| parse_error.107 | domain_error | +| parse_error.108 | domain_error | +| parse_error.109 | invalid_argument | +| parse_error.110 | out_of_range | +| parse_error.111 | invalid_argument | +| parse_error.112 | invalid_argument | +| invalid_iterator.201 | domain_error | +| invalid_iterator.202 | domain_error | +| invalid_iterator.203 | domain_error | +| invalid_iterator.204 | out_of_range | +| invalid_iterator.205 | out_of_range | +| invalid_iterator.206 | domain_error | +| invalid_iterator.207 | domain_error | +| invalid_iterator.208 | domain_error | +| invalid_iterator.209 | domain_error | +| invalid_iterator.210 | domain_error | +| invalid_iterator.211 | domain_error | +| invalid_iterator.212 | domain_error | +| invalid_iterator.213 | domain_error | +| invalid_iterator.214 | out_of_range | +| type_error.301 | domain_error | +| type_error.302 | domain_error | +| type_error.303 | domain_error | +| type_error.304 | domain_error | +| type_error.305 | domain_error | +| type_error.306 | domain_error | +| type_error.307 | domain_error | +| type_error.308 | domain_error | +| type_error.309 | domain_error | +| type_error.310 | domain_error | +| type_error.311 | domain_error | +| type_error.313 | domain_error | +| type_error.314 | domain_error | +| type_error.315 | domain_error | +| out_of_range.401 | out_of_range | +| out_of_range.402 | out_of_range | +| out_of_range.403 | out_of_range | +| out_of_range.404 | out_of_range | +| out_of_range.405 | domain_error | +| other_error.501 | domain_error | #### Handling of NaN and INF @@ -939,8 +939,8 @@ This release implements with **[CBOR](http://cbor.io)** and **[MessagePack](http - :bug: fixed an overflow detection error in the number parser - :memo: updated [contribution guidelines](https://github.com/nlohmann/json/blob/develop/.github/CONTRIBUTING.md) to a list of frequentely asked features that will most likely be never added to the library - :memo: added a **table of contents** to the [README file](https://github.com/nlohmann/json/blob/develop/README.md) to add some structure -- :memo: mentioned the many [examples](https://github.com/nlohmann/json/tree/develop/doc/examples) and the [documentation](https://nlohmann.github.io/json/) in the [README file]() -- :hammer: split [unit tests](https://github.com/nlohmann/json/tree/develop/test/src) into individual independent binaries to speed up compilation and testing +- :memo: mentioned the many [examples](https://github.com/nlohmann/json/tree/develop/docs/mkdocs/docs/examples) and the [documentation](https://nlohmann.github.io/json/) in the [README file]() +- :hammer: split [unit tests](https://github.com/nlohmann/json/tree/develop/tests/src) into individual independent binaries to speed up compilation and testing - :white_check_mark: the test suite now contains **11201886** tests ## v2.0.8 @@ -982,10 +982,10 @@ This release combines a lot of small fixes and improvements. The fixes are backw ### Summary -This release fixes a few bugs in the JSON parser found in the [Parsing JSON is a Minefield 💣](http://seriot.ch/parsing_json.html) article. The fixes are backwards compatible. +This release fixes a few bugs in the JSON parser found in the [Parsing JSON is a Minefield 💣](https://seriot.ch/projects/parsing_json.html) article. The fixes are backwards compatible. ### Changes -- The article [Parsing JSON is a Minefield 💣](http://seriot.ch/parsing_json.html) discusses a lot of pitfalls of the JSON specification. When investigating the published test cases, a few bugs in the library were found and fixed: +- The article [Parsing JSON is a Minefield 💣](https://seriot.ch/projects/parsing_json.html) discusses a lot of pitfalls of the JSON specification. When investigating the published test cases, a few bugs in the library were found and fixed: - Files with less than 5 bytes can now be parsed without error. - The library now properly rejects any file encoding other than UTF-8. Furthermore, incorrect surrogate pairs are properly detected and rejected. - The library now accepts all but one "yes" test (y_string_utf16.json): UTF-16 is not supported. @@ -1071,7 +1071,7 @@ This release combines a lot of small fixes and improvements. The release is back - Improved the performance of the serialization by avoiding the re-creation of a locale object. - Fixed two MSVC warnings. Compiling the test suite with `/Wall` now only warns about non-inlined functions (C4710) and the deprecation of the constructor from input-stream (C4996). - Some project internals: - - The project has qualified for the [Core Infrastructure Initiative Best Practices Badge](https://bestpractices.coreinfrastructure.org/projects/289). While most requirements where already satisfied, some led to a more explicit documentation of quality-ensuring procedures. For instance, static analysis is now executed with every commit on the build server. Furthermore, the [contribution guidelines document](https://github.com/nlohmann/json/blob/develop/.github/CONTRIBUTING.md) how to communicate security issues privately. + - The project has qualified for the [Core Infrastructure Initiative Best Practices Badge](https://bestpractices.coreinfrastructure.org/projects/289). While most requirements where already satisfied, some led to more explicit documentation of quality-ensuring procedures. For instance, static analysis is now executed with every commit on the build server. Furthermore, the [contribution guidelines document](https://github.com/nlohmann/json/blob/develop/.github/CONTRIBUTING.md) how to communicate security issues privately. - The test suite has been overworked and split into several files to allow for faster compilation and analysis. The execute the test suite, simply execute `make check`. - The continuous integration with [Travis](https://travis-ci.org/nlohmann/json) was extended with Clang versions 3.6.0 to 3.8.1 and now includes 18 different compiler/OS combinations. - An 11-day run of [American fuzzy lop](http://lcamtuf.coredump.cx/afl/) checked 962 million inputs on the parser and found no issue. @@ -1185,7 +1185,7 @@ As `noexcept` and `constexpr` specifier have been added to several functions, th This release fixes several small bugs and adds functionality in a backwards-compatible manner. Compared to the [last version (1.0.0)](https://github.com/nlohmann/json/releases/tag/v1.0.0), the following changes have been made: ### Changes -- _Fixed_: **Floating-point numbers** are now serialized and deserialized properly such that rountripping works in more cases. [#185, #186, #190, #191, #194] +- _Fixed_: **Floating-point numbers** are now serialized and deserialized properly such that roundtripping works in more cases. [#185, #186, #190, #191, #194] - _Added_: The code now contains **assertions** to detect undefined behavior during development. As the standard function `assert` is used, the assertions can be switched off by defining the preprocessor symbol `NDEBUG` during compilation. [#168] - _Added_: It is now possible to get a **reference** to the stored values via the newly added function `get_ref()`. [#128, #184] - _Fixed_: Access to object values via keys (**`operator[]`**) now works with all kind of string representations. [#171, #189] diff --git a/docs/mkdocs/docs/home/sponsors.md b/docs/mkdocs/docs/home/sponsors.md index 9097049d49..7e3ef02846 100644 --- a/docs/mkdocs/docs/home/sponsors.md +++ b/docs/mkdocs/docs/home/sponsors.md @@ -2,6 +2,11 @@ You can sponsor this library at [GitHub Sponsors](https://github.com/sponsors/nlohmann). +## Priority Sponsor + +- [Martti Laine](https://github.com/codeclown) +- [Paul Harrington](https://github.com/phrrngtn) + ## Named Sponsors - [Michael Hartmann](https://github.com/reFX-Mike) @@ -9,5 +14,6 @@ You can sponsor this library at [GitHub Sponsors](https://github.com/sponsors/nl - [Steve Sperandeo](https://github.com/homer6) - [Robert Jefe Lindstädt](https://github.com/eljefedelrodeodeljefe) - [Steve Wagner](https://github.com/ciroque) +- [Lion Yang](https://github.com/LionNatsu) Thanks everyone! diff --git a/docs/mkdocs/docs/images/customers.png b/docs/mkdocs/docs/images/customers.png new file mode 100644 index 0000000000..0334c4f35b Binary files /dev/null and b/docs/mkdocs/docs/images/customers.png differ diff --git a/docs/mkdocs/docs/images/json.gif b/docs/mkdocs/docs/images/json.gif new file mode 100644 index 0000000000..8b86b03757 Binary files /dev/null and b/docs/mkdocs/docs/images/json.gif differ diff --git a/docs/mkdocs/docs/images/package_managers/CPM.png b/docs/mkdocs/docs/images/package_managers/CPM.png new file mode 100644 index 0000000000..4197541a1f Binary files /dev/null and b/docs/mkdocs/docs/images/package_managers/CPM.png differ diff --git a/docs/mkdocs/docs/images/package_managers/bazel.svg b/docs/mkdocs/docs/images/package_managers/bazel.svg new file mode 100644 index 0000000000..ed5910e133 --- /dev/null +++ b/docs/mkdocs/docs/images/package_managers/bazel.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/docs/mkdocs/docs/images/package_managers/conan.svg b/docs/mkdocs/docs/images/package_managers/conan.svg new file mode 100644 index 0000000000..15bcbdc925 --- /dev/null +++ b/docs/mkdocs/docs/images/package_managers/conan.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/mkdocs/docs/images/package_managers/conda.svg b/docs/mkdocs/docs/images/package_managers/conda.svg new file mode 100644 index 0000000000..3ad2de74dc --- /dev/null +++ b/docs/mkdocs/docs/images/package_managers/conda.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/mkdocs/docs/images/package_managers/homebrew.svg b/docs/mkdocs/docs/images/package_managers/homebrew.svg new file mode 100644 index 0000000000..9d206f7c47 --- /dev/null +++ b/docs/mkdocs/docs/images/package_managers/homebrew.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/mkdocs/docs/images/package_managers/macports.svg b/docs/mkdocs/docs/images/package_managers/macports.svg new file mode 100644 index 0000000000..b09bff2550 --- /dev/null +++ b/docs/mkdocs/docs/images/package_managers/macports.svg @@ -0,0 +1,81 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/docs/mkdocs/docs/images/package_managers/meson.svg b/docs/mkdocs/docs/images/package_managers/meson.svg new file mode 100644 index 0000000000..a4136a1cf5 --- /dev/null +++ b/docs/mkdocs/docs/images/package_managers/meson.svg @@ -0,0 +1,2 @@ + +file_type_meson \ No newline at end of file diff --git a/docs/mkdocs/docs/images/package_managers/nuget.svg b/docs/mkdocs/docs/images/package_managers/nuget.svg new file mode 100644 index 0000000000..1765dcc921 --- /dev/null +++ b/docs/mkdocs/docs/images/package_managers/nuget.svg @@ -0,0 +1,21 @@ + + + + nuget + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/docs/mkdocs/docs/images/package_managers/spack.svg b/docs/mkdocs/docs/images/package_managers/spack.svg new file mode 100644 index 0000000000..a56eed57a6 --- /dev/null +++ b/docs/mkdocs/docs/images/package_managers/spack.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/mkdocs/docs/images/package_managers/swift.svg b/docs/mkdocs/docs/images/package_managers/swift.svg new file mode 100644 index 0000000000..5fe20abea1 --- /dev/null +++ b/docs/mkdocs/docs/images/package_managers/swift.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/mkdocs/docs/images/package_managers/vcpkg.png b/docs/mkdocs/docs/images/package_managers/vcpkg.png new file mode 100644 index 0000000000..76d43a83da Binary files /dev/null and b/docs/mkdocs/docs/images/package_managers/vcpkg.png differ diff --git a/docs/mkdocs/docs/images/package_managers/xmake.svg b/docs/mkdocs/docs/images/package_managers/xmake.svg new file mode 100644 index 0000000000..6bce7552cd --- /dev/null +++ b/docs/mkdocs/docs/images/package_managers/xmake.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/mkdocs/docs/integration/bazel/BUILD b/docs/mkdocs/docs/integration/bazel/BUILD new file mode 100644 index 0000000000..ec223f14d6 --- /dev/null +++ b/docs/mkdocs/docs/integration/bazel/BUILD @@ -0,0 +1,5 @@ +cc_binary( + name = "main", + srcs = ["example.cpp"], + deps = ["@nlohmann_json//:json"], +) diff --git a/docs/mkdocs/docs/integration/bazel/MODULE.bazel b/docs/mkdocs/docs/integration/bazel/MODULE.bazel new file mode 100644 index 0000000000..ba902be273 --- /dev/null +++ b/docs/mkdocs/docs/integration/bazel/MODULE.bazel @@ -0,0 +1 @@ +bazel_dep(name = "nlohmann_json", version = "3.11.3.bcr.1") diff --git a/docs/mkdocs/docs/integration/bazel/example.cpp b/docs/mkdocs/docs/integration/bazel/example.cpp new file mode 100644 index 0000000000..1a7ac4de2e --- /dev/null +++ b/docs/mkdocs/docs/integration/bazel/example.cpp @@ -0,0 +1,10 @@ +#include +#include +#include + +using json = nlohmann::json; + +int main() +{ + std::cout << std::setw(4) << json::meta() << std::endl; +} diff --git a/docs/mkdocs/docs/integration/cget/CMakeLists.txt b/docs/mkdocs/docs/integration/cget/CMakeLists.txt new file mode 100644 index 0000000000..e3fda5b9ad --- /dev/null +++ b/docs/mkdocs/docs/integration/cget/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.15) +project(json_example) + +find_package(nlohmann_json REQUIRED) + +add_executable(json_example example.cpp) +target_link_libraries(json_example PRIVATE nlohmann_json::nlohmann_json) diff --git a/docs/mkdocs/docs/integration/cget/example.cpp b/docs/mkdocs/docs/integration/cget/example.cpp new file mode 100644 index 0000000000..1a7ac4de2e --- /dev/null +++ b/docs/mkdocs/docs/integration/cget/example.cpp @@ -0,0 +1,10 @@ +#include +#include +#include + +using json = nlohmann::json; + +int main() +{ + std::cout << std::setw(4) << json::meta() << std::endl; +} diff --git a/docs/mkdocs/docs/integration/cmake.md b/docs/mkdocs/docs/integration/cmake.md index 545f53f303..b89e4dbd3f 100644 --- a/docs/mkdocs/docs/integration/cmake.md +++ b/docs/mkdocs/docs/integration/cmake.md @@ -15,10 +15,10 @@ and use the namespaced imported target from the generated package configuration: !!! example ```cmake title="CMakeLists.txt" - cmake_minimum_required(VERSION 3.1) + cmake_minimum_required(VERSION 3.5) project(ExampleProject LANGUAGES CXX) - find_package(nlohmann_json 3.11.3 REQUIRED) + find_package(nlohmann_json 3.12.0 REQUIRED) add_executable(example example.cpp) target_link_libraries(example PRIVATE nlohmann_json::nlohmann_json) @@ -35,7 +35,7 @@ To embed the library directly into an existing CMake project, place the entire s !!! example ```cmake title="CMakeLists.txt" - cmake_minimum_required(VERSION 3.1) + cmake_minimum_required(VERSION 3.5) project(ExampleProject LANGUAGES CXX) # If you only include this third party in PRIVATE source files, you do not need to install it @@ -77,7 +77,7 @@ to the following. ```cmake title="thirdparty/CMakeLists.txt" if(EXAMPLE_USE_EXTERNAL_JSON) - find_package(nlohmann_json 3.11.3 REQUIRED) + find_package(nlohmann_json 3.12.0 REQUIRED) else() set(JSON_BuildTests OFF CACHE INTERNAL "") add_subdirectory(nlohmann_json) @@ -90,7 +90,7 @@ to the following. ### FetchContent Since CMake v3.11, [FetchContent](https://cmake.org/cmake/help/v3.11/module/FetchContent.html) can be used to -automatically download a release as a dependency at configure type. +automatically download a release as a dependency at configure time. !!! example @@ -100,7 +100,7 @@ automatically download a release as a dependency at configure type. include(FetchContent) - FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz) + FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.12.0/json.tar.xz) FetchContent_MakeAvailable(json) add_executable(example example.cpp) @@ -115,13 +115,11 @@ automatically download a release as a dependency at configure type. ```cmake FetchContent_Declare(json GIT_REPOSITORY https://github.com/nlohmann/json - GIT_TAG v3.11.3 + GIT_TAG v3.12.0 ) ``` - However, the repository download size is quite large. You might want to depend on - a smaller repository. For instance, you might want to replace the URL in the example by - . + However, the repository download size is quite large. ## CMake Options @@ -137,6 +135,10 @@ Enable CI build targets. The exact targets are used during the several CI steps Enable [extended diagnostic messages](../home/exceptions.md#extended-diagnostic-messages) by defining macro [`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md). This option is `OFF` by default. +### `JSON_Diagnostic_Positions` + +Enable position diagnostics by defining macro [`JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md). This option is `OFF` by default. + ### `JSON_DisableEnumSerialization` Disable default `enum` serialization by defining the macro @@ -159,14 +161,18 @@ Enable implicit conversions by defining macro [`JSON_USE_IMPLICIT_CONVERSIONS`]( Install CMake targets during install step. This option is `ON` by default if the library's CMake project is the top project. +### `JSON_LegacyDiscardedValueComparison` + +Enable the (incorrect) legacy comparison behavior of discarded JSON values by defining macro [`JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON`](../api/macros/json_use_legacy_discarded_value_comparison.md). This option is `OFF` by default. + ### `JSON_MultipleHeaders` -Use non-amalgamated version of the library. This option is `OFF` by default. +Use the non-amalgamated version of the library. This option is `OFF` by default. ### `JSON_SystemInclude` -Treat the library headers like system headers (i.e., adding `SYSTEM` to the [`target_include_directories`](https://cmake.org/cmake/help/latest/command/target_include_directories.html) call) to checks for this library by tools like Clang-Tidy. This option is `OFF` by default. +Treat the library headers like system headers (i.e., adding `SYSTEM` to the [`target_include_directories`](https://cmake.org/cmake/help/latest/command/target_include_directories.html) call) to check for this library by tools like Clang-Tidy. This option is `OFF` by default. ### `JSON_Valgrind` -Execute test suite with [Valgrind](https://valgrind.org). This option is `OFF` by default. Depends on `JSON_BuildTests`. +Execute the test suite with [Valgrind](https://valgrind.org). This option is `OFF` by default. Depends on `JSON_BuildTests`. diff --git a/docs/mkdocs/docs/integration/conan/CMakeLists.txt b/docs/mkdocs/docs/integration/conan/CMakeLists.txt index fd3e9ca716..e3fda5b9ad 100644 --- a/docs/mkdocs/docs/integration/conan/CMakeLists.txt +++ b/docs/mkdocs/docs/integration/conan/CMakeLists.txt @@ -1,9 +1,7 @@ +cmake_minimum_required(VERSION 3.15) project(json_example) -cmake_minimum_required(VERSION 2.8.12) -add_definitions("-std=c++11") -include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) -conan_basic_setup() +find_package(nlohmann_json REQUIRED) add_executable(json_example example.cpp) -target_link_libraries(json_example ${CONAN_LIBS}) +target_link_libraries(json_example PRIVATE nlohmann_json::nlohmann_json) diff --git a/docs/mkdocs/docs/integration/conan/Conanfile.txt b/docs/mkdocs/docs/integration/conan/Conanfile.txt index a8a3e70378..d8a6295bb3 100644 --- a/docs/mkdocs/docs/integration/conan/Conanfile.txt +++ b/docs/mkdocs/docs/integration/conan/Conanfile.txt @@ -1,5 +1,6 @@ [requires] -nlohmann_json/3.7.3 +nlohmann_json/3.12.0 [generators] -cmake +CMakeToolchain +CMakeDeps diff --git a/docs/mkdocs/docs/integration/conan/example.cpp b/docs/mkdocs/docs/integration/conan/example.cpp index e5a31be4bd..1a7ac4de2e 100644 --- a/docs/mkdocs/docs/integration/conan/example.cpp +++ b/docs/mkdocs/docs/integration/conan/example.cpp @@ -1,9 +1,10 @@ #include #include +#include using json = nlohmann::json; int main() { - std::cout << json::meta() << std::endl; + std::cout << std::setw(4) << json::meta() << std::endl; } diff --git a/docs/mkdocs/docs/integration/conda/example.cpp b/docs/mkdocs/docs/integration/conda/example.cpp new file mode 100644 index 0000000000..1a7ac4de2e --- /dev/null +++ b/docs/mkdocs/docs/integration/conda/example.cpp @@ -0,0 +1,10 @@ +#include +#include +#include + +using json = nlohmann::json; + +int main() +{ + std::cout << std::setw(4) << json::meta() << std::endl; +} diff --git a/docs/mkdocs/docs/integration/cpm/CMakeLists.txt b/docs/mkdocs/docs/integration/cpm/CMakeLists.txt new file mode 100644 index 0000000000..69e36b7828 --- /dev/null +++ b/docs/mkdocs/docs/integration/cpm/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.15) +project(json_example) + +include(${CMAKE_SOURCE_DIR}/cmake/CPM.cmake) + +CPMAddPackage("gh:nlohmann/json@3.12.0") + +add_executable(json_example example.cpp) +target_link_libraries(json_example PRIVATE nlohmann_json::nlohmann_json) diff --git a/docs/mkdocs/docs/integration/cpm/example.cpp b/docs/mkdocs/docs/integration/cpm/example.cpp new file mode 100644 index 0000000000..1a7ac4de2e --- /dev/null +++ b/docs/mkdocs/docs/integration/cpm/example.cpp @@ -0,0 +1,10 @@ +#include +#include +#include + +using json = nlohmann::json; + +int main() +{ + std::cout << std::setw(4) << json::meta() << std::endl; +} diff --git a/docs/mkdocs/docs/integration/homebrew/CMakeLists.txt b/docs/mkdocs/docs/integration/homebrew/CMakeLists.txt new file mode 100644 index 0000000000..12f4ae1f2b --- /dev/null +++ b/docs/mkdocs/docs/integration/homebrew/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.15) +project(json_example) + +find_package(nlohmann_json CONFIG REQUIRED) + +add_executable(json_example example.cpp) +target_link_libraries(json_example PRIVATE nlohmann_json::nlohmann_json) diff --git a/docs/mkdocs/docs/integration/homebrew/example.cpp b/docs/mkdocs/docs/integration/homebrew/example.cpp new file mode 100644 index 0000000000..1a7ac4de2e --- /dev/null +++ b/docs/mkdocs/docs/integration/homebrew/example.cpp @@ -0,0 +1,10 @@ +#include +#include +#include + +using json = nlohmann::json; + +int main() +{ + std::cout << std::setw(4) << json::meta() << std::endl; +} diff --git a/docs/mkdocs/docs/integration/hunter/CMakeLists.txt b/docs/mkdocs/docs/integration/hunter/CMakeLists.txt new file mode 100644 index 0000000000..4acc325860 --- /dev/null +++ b/docs/mkdocs/docs/integration/hunter/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.15) + +include("cmake/HunterGate.cmake") +HunterGate( + URL "https://github.com/cpp-pm/hunter/archive/v0.23.297.tar.gz" + SHA1 "3319fe6a3b08090df7df98dee75134d68e2ef5a3" +) + +project(json_example) + +hunter_add_package(nlohmann_json) +find_package(nlohmann_json CONFIG REQUIRED) + +add_executable(json_example example.cpp) +target_link_libraries(json_example PRIVATE nlohmann_json::nlohmann_json) diff --git a/docs/mkdocs/docs/integration/hunter/example.cpp b/docs/mkdocs/docs/integration/hunter/example.cpp new file mode 100644 index 0000000000..1a7ac4de2e --- /dev/null +++ b/docs/mkdocs/docs/integration/hunter/example.cpp @@ -0,0 +1,10 @@ +#include +#include +#include + +using json = nlohmann::json; + +int main() +{ + std::cout << std::setw(4) << json::meta() << std::endl; +} diff --git a/docs/mkdocs/docs/integration/macports/CMakeLists.txt b/docs/mkdocs/docs/integration/macports/CMakeLists.txt new file mode 100644 index 0000000000..12f4ae1f2b --- /dev/null +++ b/docs/mkdocs/docs/integration/macports/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.15) +project(json_example) + +find_package(nlohmann_json CONFIG REQUIRED) + +add_executable(json_example example.cpp) +target_link_libraries(json_example PRIVATE nlohmann_json::nlohmann_json) diff --git a/docs/mkdocs/docs/integration/macports/example.cpp b/docs/mkdocs/docs/integration/macports/example.cpp new file mode 100644 index 0000000000..1a7ac4de2e --- /dev/null +++ b/docs/mkdocs/docs/integration/macports/example.cpp @@ -0,0 +1,10 @@ +#include +#include +#include + +using json = nlohmann::json; + +int main() +{ + std::cout << std::setw(4) << json::meta() << std::endl; +} diff --git a/docs/mkdocs/docs/integration/meson/example.cpp b/docs/mkdocs/docs/integration/meson/example.cpp new file mode 100644 index 0000000000..1a7ac4de2e --- /dev/null +++ b/docs/mkdocs/docs/integration/meson/example.cpp @@ -0,0 +1,10 @@ +#include +#include +#include + +using json = nlohmann::json; + +int main() +{ + std::cout << std::setw(4) << json::meta() << std::endl; +} diff --git a/docs/mkdocs/docs/integration/meson/meson.build b/docs/mkdocs/docs/integration/meson/meson.build new file mode 100644 index 0000000000..ec6d2343b1 --- /dev/null +++ b/docs/mkdocs/docs/integration/meson/meson.build @@ -0,0 +1,12 @@ +project('json_example', 'cpp', + version: '1.0', + default_options: ['cpp_std=c++11'] +) + +dependency_json = dependency('nlohmann_json', required: true) + +executable('json_example', + sources: ['example.cpp'], + dependencies: [dependency_json], + install: true +) diff --git a/docs/mkdocs/docs/integration/migration_guide.md b/docs/mkdocs/docs/integration/migration_guide.md index d250f5b20b..e81b8c08fb 100644 --- a/docs/mkdocs/docs/integration/migration_guide.md +++ b/docs/mkdocs/docs/integration/migration_guide.md @@ -187,7 +187,7 @@ conversions with calls to [`get`](../api/basic_json/get.md), [`get_to`](../api/b ```cpp nlohmann::json j = "Hello, world!"; - auto s = j.template get(); + auto s = j.get(); ``` === "Future-proof (alternative)" @@ -260,5 +260,5 @@ exact version and configuration is relevant, use macro ## Do not use the `details` namespace -The `details` namespace is not part of the public API of the library and can change in any version without announcement. -Do not rely on any function or type in the `details` namespace. +The `details` namespace is not part of the public API of the library and can change in any version without an +announcement. Do not rely on any function or type in the `details` namespace. diff --git a/docs/mkdocs/docs/integration/nuget/nuget-package-content.png b/docs/mkdocs/docs/integration/nuget/nuget-package-content.png new file mode 100644 index 0000000000..cc975b98bc Binary files /dev/null and b/docs/mkdocs/docs/integration/nuget/nuget-package-content.png differ diff --git a/docs/mkdocs/docs/integration/nuget/nuget-project-changes.png b/docs/mkdocs/docs/integration/nuget/nuget-project-changes.png new file mode 100644 index 0000000000..eb2a520a8f Binary files /dev/null and b/docs/mkdocs/docs/integration/nuget/nuget-project-changes.png differ diff --git a/docs/mkdocs/docs/integration/nuget/nuget-project-makefile.png b/docs/mkdocs/docs/integration/nuget/nuget-project-makefile.png new file mode 100644 index 0000000000..74657f264d Binary files /dev/null and b/docs/mkdocs/docs/integration/nuget/nuget-project-makefile.png differ diff --git a/docs/mkdocs/docs/integration/nuget/nuget-search-package.png b/docs/mkdocs/docs/integration/nuget/nuget-search-package.png new file mode 100644 index 0000000000..da4c6e6dc6 Binary files /dev/null and b/docs/mkdocs/docs/integration/nuget/nuget-search-package.png differ diff --git a/docs/mkdocs/docs/integration/nuget/nuget-select-package.png b/docs/mkdocs/docs/integration/nuget/nuget-select-package.png new file mode 100644 index 0000000000..b80ebc7bbb Binary files /dev/null and b/docs/mkdocs/docs/integration/nuget/nuget-select-package.png differ diff --git a/docs/mkdocs/docs/integration/package_managers.md b/docs/mkdocs/docs/integration/package_managers.md index c9a273a500..9d62c05d53 100644 --- a/docs/mkdocs/docs/integration/package_managers.md +++ b/docs/mkdocs/docs/integration/package_managers.md @@ -1,5 +1,24 @@ # Package Managers +

+![Homebrew](../images/package_managers/homebrew.svg){: style="height:1em"} [**Homebrew**](#homebrew) `nlohmann-json`   +![Meson](../images/package_managers/meson.svg){: style="height:1em"} [**Meson**](#meson) `nlohmann_json`   +![Bazel](../images/package_managers/bazel.svg){: style="height:1em"} [**Bazel**](#bazel) `nlohmann_json`
+![Conan](../images/package_managers/conan.svg){: style="height:1em"} [**Conan**](#conan) `nlohmann_json`   +![Spack](../images/package_managers/spack.svg){: style="height:1em"} [**Spack**](#spack) `nlohmann-json`   +[**Hunter**](#hunter) `nlohmann_json`
+![vcpkg](../images/package_managers/vcpkg.png){: style="height:1em"} [**vcpkg**](#vcpkg) `nlohmann-json`   +[**cget**](#cget) `nlohmann/json`   +![Swift Package Manager](../images/package_managers/swift.svg){: style="height:1em"} [**Swift Package Manager**](#swift-package-manager) `nlohmann/json`
+![Nuget](../images/package_managers/nuget.svg){: style="height:1em"} [**NuGet**](#nuget) `nlohmann.json`   +![Conda](../images/package_managers/conda.svg){: style="height:1em"} [**Conda**](#conda) `nlohmann_json`   +![MacPorts](../images/package_managers/macports.svg){: style="height:1em"} [**MacPorts**](#macports) `nlohmann-json`
+![cpm.cmake](../images/package_managers/CPM.png){: style="height:1em"} [**CPM.cmake**](#cpmcmake) `gh:nlohmann/json` +![xmake](../images/package_managers/xmake.svg){: style="height:1em"} [**xmake**](#xmake) `nlohmann_json` +

+ +## Running example + Throughout this page, we will describe how to compile the example file `example.cpp` below. ```cpp @@ -14,61 +33,177 @@ When executed, this program should create output similar to ## Homebrew -If you are using OS X and [Homebrew](http://brew.sh), just type +!!! abstract "Summary" -```sh -brew install nlohmann-json -``` + formula: [**`nlohmann-json`**](https://formulae.brew.sh/formula/nlohmann-json) + + - [![Homebrew package](https://repology.org/badge/version-for-repo/homebrew/nlohmann-json.svg)](https://repology.org/project/nlohmann-json/versions) + - :octicons-tag-24: Available versions: current version and development version (with `--HEAD` parameter) + - :octicons-rocket-24: The formula is updated with every release. + - :octicons-person-24: Maintainer: Niels Lohmann + - :octicons-file-24: File issues at the [Homebrew issue tracker](https://github.com/Homebrew/homebrew-core/issues) + - :octicons-question-24: [Homebrew website](https://brew.sh) -and you're set. If you want the bleeding edge rather than the latest release, use +If you are using [Homebrew](http://brew.sh), you can install the library with ```sh -brew install nlohmann-json --HEAD +brew install nlohmann-json ``` -instead. See [nlohmann-json](https://formulae.brew.sh/formula/nlohmann-json) for more information. +The header can be used directly in your code or via CMake. -??? example +??? example "Example: Raw compilation" 1. Create the following file: ```cpp title="example.cpp" - --8<-- "integration/example.cpp" + --8<-- "integration/homebrew/example.cpp" ``` - 2. Install the package + 2. Install the package: ```sh brew install nlohmann-json ``` - 3. Determine the include path, which defaults to `/usr/local/Cellar/nlohmann-json/$version/include`, where `$version` is the version of the library, e.g. `3.7.3`. The path of the library can be determined with + 3. Compile the code and pass the Homebrew prefix to the include path such that the library can be found: ```sh - brew list nlohmann-json + c++ example.cpp -I$(brew --prefix nlohmann-json)/include -std=c++11 -o example ``` - 4. Compile the code. For instance, the code can be compiled using Clang with +??? example "Example: CMake" + + 1. Create the following files: + + ```cpp title="example.cpp" + --8<-- "integration/homebrew/example.cpp" + ``` + + ```cmake title="CMakeLists.txt" + --8<-- "integration/homebrew/CMakeLists.txt" + ``` + + 2. Install the package: ```sh - clang++ example.cpp -I/usr/local/Cellar/nlohmann-json/3.7.3/include -std=c++11 -o example + brew install nlohmann-json ``` -:material-update: The [formula](https://formulae.brew.sh/formula/nlohmann-json) is updated automatically. + 3. Compile the code and pass the Homebrew prefix to CMake to find installed packages via `#!cmake find_package`: + + ```sh + CMAKE_PREFIX_PATH=$(brew --prefix) cmake -S . -B build + cmake --build build + ``` ## Meson -If you are using the [Meson Build System](http://mesonbuild.com), add this source tree as a [meson subproject](https://mesonbuild.com/Subprojects.html#using-a-subproject). You may also use the `include.zip` published in this project's [Releases](https://github.com/nlohmann/json/releases) to reduce the size of the vendored source tree. Alternatively, you can get a wrap file by downloading it from [Meson WrapDB](https://wrapdb.mesonbuild.com/nlohmann_json), or simply use `meson wrap install nlohmann_json`. Please see the meson project for any issues regarding the packaging. +!!! abstract "Summary" + + wrap: **`nlohmann_json`** + + - :octicons-tag-24: Available versions: current version and select older versions (see + [WrapDB](https://mesonbuild.com/Wrapdb-projects.html)) + - :octicons-rocket-24: The package is update automatically from file + [`meson.build`](https://github.com/nlohmann/json/blob/develop/meson.build). + - :octicons-file-24: File issues at the [library issue tracker](https://github.com/nlohmann/json/issues) + - :octicons-question-24: [Meson website](https://mesonbuild.com/index.html) + +If you are using the [Meson Build System](http://mesonbuild.com), add this source tree as a [meson subproject](https://mesonbuild.com/Subprojects.html#using-a-subproject). You may also use the +`include.zip` published in this project's [Releases](https://github.com/nlohmann/json/releases) to reduce the size of the vendored source tree. Alternatively, +you can get a wrap file by downloading it from [Meson WrapDB](https://mesonbuild.com/Wrapdb-projects.html), or use + +```shell +meson wrap install nlohmann_json +``` + +Please see the Meson project for any issues regarding the packaging. + +The provided `meson.build` can also be used as an alternative to CMake for installing `nlohmann_json` system-wide in +which case a pkg-config file is installed. To use it, have your build system require the `nlohmann_json` +pkg-config dependency. In Meson, it is preferred to use the +[`dependency()`](https://mesonbuild.com/Reference-manual.html#dependency) object with a subproject fallback, rather than +using the subproject directly. + +??? example "Example: Wrap" + + 1. Create the following files: + + ```ini title="meson.build" + --8<-- "integration/meson/meson.build" + ``` + + ```cpp title="example.cpp" + --8<-- "integration/meson/example.cpp" + ``` + + 2. Use the Meson WrapDB to fetch the nlohmann/json wrap: + + ```shell + mkdir subprojects + meson wrap install nlohmann_json + ``` + + 3. Build: -The provided `meson.build` can also be used as an alternative to cmake for installing `nlohmann_json` system-wide in which case a pkg-config file is installed. To use it, simply have your build system require the `nlohmann_json` pkg-config dependency. In Meson, it is preferred to use the [`dependency()`](https://mesonbuild.com/Reference-manual.html#dependency) object with a subproject fallback, rather than using the subproject directly. + ```shell + meson setup build + meson compile -C build + ``` ## Bazel -This repository provides a [Bazel](https://bazel.build/) `WORKSPACE.bazel` and a corresponding `BUILD.bazel` file. Therefore, this repository can be referenced by workspace rules such as `http_archive`, `git_repository`, or `local_repository` from other Bazel workspaces. To use the library you only need to depend on the target `@nlohmann_json//:json` (e.g. via `deps` attribute). +!!! abstract "Summary" + + use `bazel_dep`, `git_override`, or `local_path_override` + + - :octicons-tag-24: Any version, that is available via [Bazel Central Registry](https://registry.bazel.build/modules/nlohmann_json) + - :octicons-file-24: File issues at the [library issue tracker](https://github.com/nlohmann/json/issues) + - :octicons-question-24: [Bazel website](https://bazel.build) + +This repository provides a [Bazel](https://bazel.build/) `MODULE.bazel` and a corresponding `BUILD.bazel` file. Therefore, this +repository can be referenced within a `MODULE.bazel` by rules such as `archive_override`, `git_override`, or `local_path_override`. To use the library, you need to depend on the target `@nlohmann_json//:json` (i.e., via `deps` attribute). + +??? example + + 1. Create the following files: + + ```ini title="BUILD" + --8<-- "integration/bazel/BUILD" + ``` + + ```ini title="WORKSPACE" + --8<-- "integration/bazel/MODULE.bazel" + ``` + + ```cpp title="example.cpp" + --8<-- "integration/bazel/example.cpp" + ``` + + 2. Build and run: + + ```shell + bazel build //:main + bazel run //:main + ``` ## Conan -If you are using [Conan](https://www.conan.io/) to manage your dependencies, merely add `nlohmann_json/x.y.z` to your `conanfile`'s requires, where `x.y.z` is the release version you want to use. Please file issues [here](https://github.com/conan-io/conan-center-index/issues) if you experience problems with the packages. +!!! abstract "Summary" + + recipe: [**`nlohmann_json`**](https://conan.io/center/recipes/nlohmann_json) + + - [![ConanCenter package](https://repology.org/badge/version-for-repo/conancenter/nlohmann-json.svg)](https://repology.org/project/nlohmann-json/versions) + - :octicons-tag-24: Available versions: current version and older versions (see + [Conan Center](https://conan.io/center/recipes/nlohmann_json)) + - :octicons-rocket-24: The package is update automatically via + [this recipe](https://github.com/conan-io/conan-center-index/tree/master/recipes/nlohmann_json). + - :octicons-file-24: File issues at the [Conan Center issue tracker](https://github.com/conan-io/conan-center-index/issues) + - :octicons-question-24: [Conan website](https://conan.io) + +If you are using [Conan](https://www.conan.io/) to manage your dependencies, merely add `nlohmann_json/x.y.z` to your `conanfile`'s +requires, where `x.y.z` is the release version you want to use. ??? example @@ -86,33 +221,140 @@ If you are using [Conan](https://www.conan.io/) to manage your dependencies, mer --8<-- "integration/conan/example.cpp" ``` - 2. Build: + 2. Call Conan: ```sh - mkdir build - cd build - conan install .. - cmake .. - cmake --build . + conan install . --output-folder=build --build=missing ``` -:material-update: The [package](https://conan.io/center/nlohmann_json) is updated automatically. + 3. Build: + + ```sh + cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" -DCMAKE_BUILD_TYPE=Release + cmake --build build + ``` ## Spack -If you are using [Spack](https://www.spack.io/) to manage your dependencies, you can use the [`nlohmann-json` package](https://spack.readthedocs.io/en/latest/package_list.html#nlohmann-json). Please see the [spack project](https://github.com/spack/spack) for any issues regarding the packaging. +!!! abstract "Summary" + + package: [**`nlohmann-json`**](https://packages.spack.io/package.html?name=nlohmann-json) + + - [![Spack package](https://repology.org/badge/version-for-repo/spack/nlohmann-json.svg)](https://repology.org/project/nlohmann-json/versions) + - :octicons-tag-24: Available versions: current version and older versions (see + [Spack package](https://packages.spack.io/package.html?name=nlohmann-json)) + - :octicons-rocket-24: The package is updated with every release. + - :octicons-person-24: Maintainer: [Axel Huebl](https://github.com/ax3l) + - :octicons-file-24: File issues at the [Spack issue tracker](https://github.com/spack/spack/issues) + - :octicons-question-24: [Spack website](https://spack.io) + +If you are using [Spack](https://www.spack.io/) to manage your dependencies, you can use the +[`nlohmann-json` package](https://packages.spack.io/package.html?name=nlohmann-json) via + +```shell +spack install nlohmann-json +``` + +Please see the [Spack project](https://github.com/spack/spack) for any issues regarding the packaging. + +??? example + + 1. Create the following files: + + ```cmake title="CMakeLists.txt" + --8<-- "integration/spack/CMakeLists.txt" + ``` + + ```cpp title="example.cpp" + --8<-- "integration/spack/example.cpp" + ``` + + 2. Install the library: + + ```sh + spack install nlohmann-json + ``` + + 3. Load the environment for your Spack-installed packages: + + ```sh + spack load nlohmann-json + ``` + + 4. Build the project with CMake: + + ```sh + cmake -S . -B build -DCMAKE_PREFIX_PATH=$(spack location -i nlohmann-json) + cmake --build build + ``` ## Hunter -If you are using [hunter](https://github.com/cpp-pm/hunter) on your project for external dependencies, then you can use the [nlohmann_json package](https://hunter.readthedocs.io/en/latest/packages/pkg/nlohmann_json.html). Please see the hunter project for any issues regarding the packaging. +!!! abstract "Summary" -## Buckaroo + package: [**`nlohmann_json`**](https://hunter.readthedocs.io/en/latest/packages/pkg/nlohmann_json.html) + + - :octicons-tag-24: Available versions: current version and older versions (see + [Hunter package](https://hunter.readthedocs.io/en/latest/packages/pkg/nlohmann_json.html)) + - :octicons-rocket-24: The package is updated with every release. + - :octicons-file-24: File issues at the [Hunter issue tracker](https://github.com/cpp-pm/hunter/issues) + - :octicons-question-24: [Hunter website](https://hunter.readthedocs.io/en/latest/) -If you are using [Buckaroo](https://buckaroo.pm), you can install this library's module with `buckaroo add github.com/buckaroo-pm/nlohmann-json`. Please file issues [here](https://github.com/buckaroo-pm/nlohmann-json). There is a demo repo [here](https://github.com/njlr/buckaroo-nholmann-json-example). +If you are using [Hunter](https://github.com/cpp-pm/hunter) on your project for external dependencies, then you can use +the [nlohmann_json package](https://hunter.readthedocs.io/en/latest/packages/pkg/nlohmann_json.html) via + +```cmake +hunter_add_package(nlohmann_json) +``` + +Please see the Hunter project for any issues regarding the packaging. + +??? example + + 1. Create the following files: + + ```cmake title="CMakeLists.txt" + --8<-- "integration/hunter/CMakeLists.txt" + ``` + + ```cpp title="example.cpp" + --8<-- "integration/hunter/example.cpp" + ``` + + 2. Download required files + + ```shell + mkdir cmake + wget https://raw.githubusercontent.com/cpp-pm/gate/master/cmake/HunterGate.cmake -O cmake/HunterGate.cmake + ``` + + 3. Build the project with CMake: + + ```shell + cmake -S . -B build + cmake --build build + ``` ## vcpkg -If you are using [vcpkg](https://github.com/Microsoft/vcpkg/) on your project for external dependencies, then you can install the [nlohmann-json package](https://github.com/Microsoft/vcpkg/tree/master/ports/nlohmann-json) with `vcpkg install nlohmann-json` and follow the then displayed descriptions. Please see the vcpkg project for any issues regarding the packaging. +!!! abstract "Summary" + + package: [**`nlohmann-json`**](https://github.com/Microsoft/vcpkg/tree/master/ports/nlohmann-json) + + - [![Vcpkg package](https://repology.org/badge/version-for-repo/vcpkg/nlohmann-json.svg)](https://repology.org/project/nlohmann-json/versions) + - :octicons-tag-24: Available versions: current version + - :octicons-rocket-24: The package is updated with every release. + - :octicons-file-24: File issues at the [vcpkg issue tracker](https://github.com/microsoft/vcpkg/issues) + - :octicons-question-24: [vcpkg website](https://vcpkg.io/) + +If you are using [vcpkg](https://github.com/Microsoft/vcpkg/) on your project for external dependencies, then you can +install the [nlohmann-json package](https://github.com/Microsoft/vcpkg/tree/master/ports/nlohmann-json) with + +```shell +vcpkg install nlohmann-json +``` + +and follow the then displayed descriptions. Please see the vcpkg project for any issues regarding the packaging. ??? example @@ -135,64 +377,450 @@ If you are using [vcpkg](https://github.com/Microsoft/vcpkg/) on your project fo 3. Build: ```sh - mkdir build - cd build - cmake .. -DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake - cmake --build . + cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake + cmake --build build ``` - Note you need to adjust `/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake` to your system. - ## cget -If you are using [cget](http://cget.readthedocs.io/en/latest/), you can install the latest development version with `cget install nlohmann/json`. A specific version can be installed with `cget install nlohmann/json@v3.1.0`. Also, the multiple header version can be installed by adding the `-DJSON_MultipleHeaders=ON` flag (i.e., `cget install nlohmann/json -DJSON_MultipleHeaders=ON`). +!!! abstract "Summary" -:material-update: cget reads directly from the [GitHub repository](https://github.com/nlohmann/json) and is always up-to-date. + package: [**`nlohmann/json`**](https://github.com/pfultz2/cget-recipes/blob/master/recipes/nlohmann/json/package.txt) -## CocoaPods + - :octicons-tag-24: Available versions: current version and older versions + - :octicons-rocket-24: The package is updated with every release. + - :octicons-file-24: File issues at the [cget issue tracker](https://github.com/pfultz2/cget-recipes/issues) + - :octicons-question-24: [cget website](https://cget.readthedocs.io/) -If you are using [CocoaPods](https://cocoapods.org), you can use the library by adding pod `"nlohmann_json", '~>3.1.2'` to your podfile (see [an example](https://bitbucket.org/benman/nlohmann_json-cocoapod/src/master/)). Please file issues [here](https://bitbucket.org/benman/nlohmann_json-cocoapod/issues?status=new&status=open). +If you are using [cget](http://cget.readthedocs.io/en/latest/), you can install the latest `master` version with + +```shell +cget install nlohmann/json +``` + +A specific version can be installed with `cget install nlohmann/json@v3.12.0`. Also, the multiple header version can be +installed by adding the `-DJSON_MultipleHeaders=ON` flag (i.e., `cget install nlohmann/json -DJSON_MultipleHeaders=ON`). + +??? example + + 1. Create the following files: + + ```cmake title="CMakeLists.txt" + --8<-- "integration/vcpkg/CMakeLists.txt" + ``` + + ```cpp title="example.cpp" + --8<-- "integration/vcpkg/example.cpp" + ``` + + 2. Initialize cget + + ```shell + cget init + ``` + + 3. Install the library + + ```shell + cget install nlohmann/json + ``` + + 4. Build + + ```shell + cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=cget/cget/cget.cmake + cmake --build build + ``` + +## Swift Package Manager + +!!! abstract "Summary" + + package: **`nlohmann/json`** + + - :octicons-tag-24: Available versions: current version and older versions + - :octicons-rocket-24: The package is updated with every release. + - :octicons-file-24: File issues at the [library issue tracker](https://github.com/nlohmann/json/issues) + - :octicons-question-24: [Xcode documentation](https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app) ## NuGet -If you are using [NuGet](https://www.nuget.org), you can use the package [nlohmann.json](https://www.nuget.org/packages/nlohmann.json/). Please check [this extensive description](https://github.com/nlohmann/json/issues/1132#issuecomment-452250255) on how to use the package. Please file issues [here](https://github.com/hnkb/nlohmann-json-nuget/issues). +!!! abstract "Summary" + + package: [**`nlohmann.json`**](https://www.nuget.org/packages/nlohmann.json/) + + - :octicons-tag-24: Available versions: current and previous versions + - :octicons-rocket-24: The package is updated with every release. + - :octicons-person-24: Maintainer: [Hani Kaabi](https://github.com/hnkb) + - :octicons-file-24: File issues at the [maintainer's issue tracker](https://github.com/hnkb/nlohmann-json-nuget/issues) + - :octicons-question-24: [NuGet website](https://www.nuget.org) + +If you are using [NuGet](https://www.nuget.org), you can use the package [nlohmann.json](https://www.nuget.org/packages/nlohmann.json/) +with + +```shell +dotnet add package nlohmann.json +``` + +??? example + + Probably the easiest way to use NuGet packages is through Visual Studio graphical interface. Just right-click on a + project (any C++ project would do) in “Solution Explorer” and select “Manage NuGet Packages…” + + ![](nuget/nuget-search-package.png) + + Now you can click on “Browse” tab and find the package you like to install. + + ![](nuget/nuget-select-package.png) + + Most of the packages in NuGet gallery are .NET packages and would not be useful in a C++ project. Microsoft + recommends adding “native” and “nativepackage” tags to C++ NuGet packages to distinguish them, but even adding + “native” to search query would still show many .NET-only packages in the list. + + Nevertheless, after finding the package you want, click on “Install” button and accept confirmation dialogs. + After the package is successfully added to the projects, you should be able to build and execute the project + without the need for making any more changes to build settings. + + !!! note + + A few notes: + + - NuGet packages are installed per project and not system-wide. The header and binaries for the package are only + available to the project it is added to, and not other projects (obviously unless we add the package to those + projects as well) + - One of the many great things about your elegant work is that it is a header-only library, which makes + deployment very straightforward. In case of libraries which need binary deployment (`.lib`, `.dll` and `.pdb` + for debug info) the different binaries for each supported compiler version must be added to the NuGet package. + Some library creators cram binary versions for all supported Visual C++ compiler versions in the same package, + so a single package will support all compilers. Some others create a different package for each compiler + version (and you usually see things like “v140” or “vc141” in package name to clarify which VC++ compiler this + package supports). + - Packages can have dependency to other packages, and in this case, NuGet will install all dependencies as well + as the requested package recursively. + + **What happens behind the scenes** + + After you add a NuGet package, three changes occur in the project source directory. Of course, we could make these + changes manually instead of using GUI: + + ![](nuget/nuget-project-changes.png) + + 1. A `packages.config` file will be created (or updated to include the package name if one such file already + exists). This file contains a list of the packages required by this project (name and minimum version) and must + be added to the project source code repository, so if you move the source code to a new machine, MSBuild/NuGet + knows which packages it has to restore (which it does automatically before each build). + + ```xml + + + + + ``` + + 2. A `packages` folder which contains actual files in the packages (these are header and binary files required for + a successful build, plus a few metadata files). In case of this library for example, it contains `json.hpp`: + + ![](nuget/nuget-package-content.png) + + !!! note + + This directory should not be added to the project source code repository, as it will be restored before each + build by MSBuild/NuGet. If you go ahead and delete this folder, then build the project again, it will + magically re-appear! + + 3. Project MSBuild makefile (which for Visual C++ projects has a .vcxproj extension) will be updated to include + settings from the package. + + ![](nuget/nuget-project-makefile.png) + + The important bit for us here is line 170, which tells MSBuild to import settings from + `packages\nlohmann.json.3.5.0\build\native\nlohmann.json.targets` file. This is a file the package creator + created and added to the package (you can see it is one of the two files I created in this repository, the other + just contains package attributes like name and version number). What does it contain? + + For our header-only repository, the only setting we need is to add our include directory to the list of + `AdditionalIncludeDirectories`: + + ```xml + + + + + $(MSBuildThisFileDirectory)include;%(AdditionalIncludeDirectories) + + + + ``` + + For libraries with binary files, we will need to add `.lib` files to linker inputs and add settings to copy + `.dll` and other redistributable files to output directory, if needed. + + There are other changes to the makefile as well: + + - Lines 165-167 add the `packages.config` as one of project files (so it is shown in Solution Explorer tree + view). It is added as None (no build action) and removing it wouldn’t affect build. + + - Lines 172-177 check to ensure the required packages are present. This will display a build error if package + directory is empty (for example when NuGet cannot restore packages because Internet connection is down). + Again, if you omit this section, the only change in build would be a more cryptic error message if build + fails. + + !!! note + + Changes to .vcxproj makefile should also be added to project source code repository. + + As you can see, the mechanism NuGet uses to modify project settings is through MSBuild makefiles, so using NuGet + with other build systems and compilers (like CMake) as a dependency manager is either impossible or more problematic + than useful. + +Please refer to [this extensive description](https://github.com/nlohmann/json/issues/1132#issuecomment-452250255) for +more information. ## Conda -If you are using [conda](https://conda.io/), you can use the package [nlohmann_json](https://github.com/conda-forge/nlohmann_json-feedstock) from [conda-forge](https://conda-forge.org) executing `conda install -c conda-forge nlohmann_json`. Please file issues [here](https://github.com/conda-forge/nlohmann_json-feedstock/issues). +!!! abstract "Summary" + + package: [**`nlohmann_json`**](https://anaconda.org/conda-forge/nlohmann_json) + + - ![](https://img.shields.io/conda/v/conda-forge/nlohmann_json) + - :octicons-tag-24: Available versions: current and previous versions + - :octicons-rocket-24: The package is updated with every release. + - :octicons-file-24: File issues at the [feedstock's issue tracker](https://github.com/conda-forge/nlohmann_json-feedstock/issues) + - :octicons-question-24: [Conda documentation](https://docs.conda.io/projects/conda/en/stable/user-guide/getting-started.html) + +If you are using [conda](https://conda.io/), you can use the package +[nlohmann_json](https://anaconda.org/conda-forge/nlohmann_json) from [conda-forge](https://conda-forge.org) executing + +```shell +conda install -c conda-forge nlohmann_json +``` + +??? example + + 1. Create the following file: + + ```cpp title="example.cpp" + --8<-- "integration/conda/example.cpp" + ``` + + 2. Create and activate an anvironment "json`: + + ```shell + conda create -n json + conda activate json + ``` + + 3. Install the package: + + ```shell + conda install -c conda-forge nlohmann_json + ``` + + 4. Build the code: + + ```shell + g++ -std=c++11 -I$(conda info --base)/envs/json/include example.cpp -o example + ``` ## MSYS2 -If you are using [MSYS2](http://www.msys2.org/), you can use the [mingw-w64-nlohmann-json](https://packages.msys2.org/base/mingw-w64-nlohmann-json) package, just type `pacman -S mingw-w64-i686-nlohmann-json` or `pacman -S mingw-w64-x86_64-nlohmann-json` for installation. Please file issues [here](https://github.com/msys2/MINGW-packages/issues/new?title=%5Bnlohmann-json%5D) if you experience problems with the packages. +If you are using [MSYS2](http://www.msys2.org/), you can use the [mingw-w64-nlohmann-json](https://packages.msys2.org/base/mingw-w64-nlohmann-json) package, type `pacman -S mingw-w64-i686-nlohmann-json` or `pacman -S mingw-w64-x86_64-nlohmann-json` for installation. Please file issues [here](https://github.com/msys2/MINGW-packages/issues/new?title=%5Bnlohmann-json%5D) if you experience problems with the packages. + +[![MSYS2 clang64 package](https://repology.org/badge/version-for-repo/msys2_clang64/nlohmann-json.svg)](https://repology.org/project/nlohmann-json/versions) +[![MSYS2 clangarm64 package](https://repology.org/badge/version-for-repo/msys2_clangarm64/nlohmann-json.svg)](https://repology.org/project/nlohmann-json/versions) +[![MSYS2 mingw package](https://repology.org/badge/version-for-repo/msys2_mingw/nlohmann-json.svg)](https://repology.org/project/nlohmann-json/versions) +[![MSYS2 ucrt64 package](https://repology.org/badge/version-for-repo/msys2_ucrt64/nlohmann-json.svg)](https://repology.org/project/nlohmann-json/versions) :material-update: The [package](https://packages.msys2.org/base/mingw-w64-nlohmann-json) is updated automatically. ## MacPorts -If you are using [MacPorts](https://ports.macports.org), execute `sudo port install nlohmann-json` to install the [nlohmann-json](https://ports.macports.org/port/nlohmann-json/) package. +!!! abstract "Summary" + + port: [**`nlohmann-json`**](https://ports.macports.org/port/nlohmann-json/) + + - [![MacPorts package](https://repology.org/badge/version-for-repo/macports/nlohmann-json.svg)](https://repology.org/project/nlohmann-json/versions) + - :octicons-tag-24: Available versions: current version + - :octicons-rocket-24: The port is updated with every release. + - :octicons-file-24: File issues at the [MacPorts issue tracker](https://trac.macports.org/newticket?port=nlohmann-json) + - :octicons-question-24: [MacPorts website](https://www.macports.org) + +If you are using [MacPorts](https://ports.macports.org), execute + +```shell +sudo port install nlohmann-json +``` + +to install the [nlohmann-json](https://ports.macports.org/port/nlohmann-json/) package. + +??? example "Example: Raw compilation" + + 1. Create the following file: + + ```cpp title="example.cpp" + --8<-- "integration/macports/example.cpp" + ``` + + 2. Install the package: + + ```sh + sudo port install nlohmann-json + ``` + + 3. Compile the code and pass the Homebrew prefix to the include path such that the library can be found: + + ```sh + c++ example.cpp -I/opt/local/include -std=c++11 -o example + ``` + +??? example "Example: CMake" + + 1. Create the following files: -:material-update: The [package](https://ports.macports.org/port/nlohmann-json/) is updated automatically. + ```cpp title="example.cpp" + --8<-- "integration/homebrew/example.cpp" + ``` + + ```cmake title="CMakeLists.txt" + --8<-- "integration/homebrew/CMakeLists.txt" + ``` + + 2. Install the package: + + ```sh + sudo port install nlohmann-json + ``` + + 3. Compile the code: + + ```sh + cmake -S . -B build + cmake --build build + ``` ## build2 -If you are using [`build2`](https://build2.org), you can use the [`nlohmann-json`](https://cppget.org/nlohmann-json) package from the public repository or directly from the [package's sources repository](https://github.com/build2-packaging/nlohmann-json). In your project's `manifest` file, just add `depends: nlohmann-json` (probably with some [version constraints](https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml#guide-add-remove-deps)). If you are not familiar with using dependencies in `build2`, [please read this introduction](https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml). +If you are using [`build2`](https://build2.org), you can use the [`nlohmann-json`](https://cppget.org/nlohmann-json) +package from the public repository or directly from the +[package's sources repository](https://github.com/build2-packaging/nlohmann-json). In your project's `manifest` file, +add `depends: nlohmann-json` (probably with some [version constraints](https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml#guide-add-remove-deps)). If you are not familiar with using dependencies in `build2`, [please read this introduction](https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml). Please file issues [here](https://github.com/build2-packaging/nlohmann-json) if you experience problems with the packages. :material-update: The [package](https://cppget.org/nlohmann-json) is updated automatically. -## wsjcpp +```shell +bdep new -t exe -l c++ +``` + +## CPM.cmake -If you are using [`wsjcpp`](http://wsjcpp.org), you can use the command `wsjcpp install "https://github.com/nlohmann/json:develop"` to get the latest version. Note you can change the branch ":develop" to an existing tag or another branch. +!!! abstract "Summary" -:material-update: wsjcpp reads directly from the [GitHub repository](https://github.com/nlohmann/json) and is always up-to-date. + package: **`gh:nlohmann/json`** -## CPM.cmake + - :octicons-tag-24: Available versions: current and previous versions + - :octicons-rocket-24: The package is updated with every release. + - :octicons-file-24: File issues at the [CPM.cmake issue tracker](https://github.com/cpm-cmake/CPM.cmake/issues) + - :octicons-question-24: [CPM.cmake website](https://github.com/cpm-cmake/CPM.cmake) -If you are using [`CPM.cmake`](https://github.com/TheLartians/CPM.cmake), you can check this [`example`](https://github.com/TheLartians/CPM.cmake/tree/master/examples/json). After [adding CPM script](https://github.com/TheLartians/CPM.cmake#adding-cpm) to your project, implement the following snippet to your CMake: +If you are using [`CPM.cmake`](https://github.com/TheLartians/CPM.cmake), add the +[CPM.cmake script](https://github.com/TheLartians/CPM.cmake#adding-cpm) and the following snippet to your CMake project: ```cmake -CPMAddPackage( - NAME nlohmann_json - GITHUB_REPOSITORY nlohmann/json - VERSION 3.9.1) +CPMAddPackage("gh:nlohmann/json@3.12.0") ``` + +??? example + + 1. Create the following files: + + ```cpp title="example.cpp" + --8<-- "integration/cpm/example.cpp" + ``` + + ```cmake title="CMakeLists.txt" + --8<-- "integration/cpm/CMakeLists.txt" + ``` + + 2. Download CPM.cmake + + ```shell + mkdir -p cmake + wget -O cmake/CPM.cmake https://github.com/cpm-cmake/CPM.cmake/releases/latest/download/get_cpm.cmake + ``` + + 3. Build + + ```shell + cmake -S . -B build + cmake --build build + ``` + +## xmake + +!!! abstract "Summary" + + package: [**`nlohmann_json`**](https://github.com/xmake-io/xmake-repo/blob/master/packages/n/nlohmann_json/xmake.lua) + + - :octicons-tag-24: Available versions: current and previous versions + - :octicons-rocket-24: The package is updated with every release. + - :octicons-file-24: File issues at the [xmake issue tracker](https://github.com/xmake-io/xmake-repo/issues) + - :octicons-question-24: [xmake website](https://xmake.io/#/) + +??? example + + 1. Create the following files: + + ```cpp title="example.cpp" + --8<-- "integration/xmake/example.cpp" + ``` + + ```lua title="xmake.lua" + --8<-- "integration/xmake/xmake.lua" + ``` + + 2. Build + + ```shell + xmake + ``` + + 3. Run + + ```shell + xmake run + ``` + +* * * + +## Other package managers + +The library is also contained in many other package repositories: [![Packaging status](https://repology.org/badge/tiny-repos/nlohmann-json.svg)](https://repology.org/project/nlohmann-json/versions) + +??? example "Package version overview" + + [![Packaging status](https://repology.org/badge/vertical-allrepos/nlohmann-json.svg)](https://repology.org/project/nlohmann-json/versions) + + +* * * + +## Buckaroo + +If you are using [Buckaroo](https://buckaroo.pm), you can install this library's module with `buckaroo add github.com/buckaroo-pm/nlohmann-json`. There is a demo repo [here](https://github.com/njlr/buckaroo-nholmann-json-example). + +!!! warning + + The module is outdated as the respective [repository](https://github.com/buckaroo-pm/nlohmann-json) has not been + updated in years. + +## CocoaPods + +If you are using [CocoaPods](https://cocoapods.org), you can use the library by adding pod `"nlohmann_json", '~>3.1.2'` +to your podfile (see [an example](https://bitbucket.org/benman/nlohmann_json-cocoapod/src/master/)). Please file issues +[here](https://bitbucket.org/benman/nlohmann_json-cocoapod/issues?status=new&status=open). + +![](https://img.shields.io/cocoapods/v/nlohmann_json) + +!!! warning + + The module is outdated as the respective [pod](https://cocoapods.org/pods/nlohmann_json) has not been updated in years. diff --git a/docs/mkdocs/docs/integration/spack/CMakeLists.txt b/docs/mkdocs/docs/integration/spack/CMakeLists.txt new file mode 100644 index 0000000000..e3fda5b9ad --- /dev/null +++ b/docs/mkdocs/docs/integration/spack/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.15) +project(json_example) + +find_package(nlohmann_json REQUIRED) + +add_executable(json_example example.cpp) +target_link_libraries(json_example PRIVATE nlohmann_json::nlohmann_json) diff --git a/docs/mkdocs/docs/integration/spack/example.cpp b/docs/mkdocs/docs/integration/spack/example.cpp new file mode 100644 index 0000000000..1a7ac4de2e --- /dev/null +++ b/docs/mkdocs/docs/integration/spack/example.cpp @@ -0,0 +1,10 @@ +#include +#include +#include + +using json = nlohmann::json; + +int main() +{ + std::cout << std::setw(4) << json::meta() << std::endl; +} diff --git a/docs/mkdocs/docs/integration/vcpkg/CMakeLists.txt b/docs/mkdocs/docs/integration/vcpkg/CMakeLists.txt index d31f4e8359..12f4ae1f2b 100644 --- a/docs/mkdocs/docs/integration/vcpkg/CMakeLists.txt +++ b/docs/mkdocs/docs/integration/vcpkg/CMakeLists.txt @@ -1,5 +1,5 @@ +cmake_minimum_required(VERSION 3.15) project(json_example) -cmake_minimum_required(VERSION 2.8.12) find_package(nlohmann_json CONFIG REQUIRED) diff --git a/docs/mkdocs/docs/integration/vcpkg/example.cpp b/docs/mkdocs/docs/integration/vcpkg/example.cpp index e5a31be4bd..1a7ac4de2e 100644 --- a/docs/mkdocs/docs/integration/vcpkg/example.cpp +++ b/docs/mkdocs/docs/integration/vcpkg/example.cpp @@ -1,9 +1,10 @@ #include #include +#include using json = nlohmann::json; int main() { - std::cout << json::meta() << std::endl; + std::cout << std::setw(4) << json::meta() << std::endl; } diff --git a/docs/mkdocs/docs/integration/xmake/example.cpp b/docs/mkdocs/docs/integration/xmake/example.cpp new file mode 100644 index 0000000000..1a7ac4de2e --- /dev/null +++ b/docs/mkdocs/docs/integration/xmake/example.cpp @@ -0,0 +1,10 @@ +#include +#include +#include + +using json = nlohmann::json; + +int main() +{ + std::cout << std::setw(4) << json::meta() << std::endl; +} diff --git a/docs/mkdocs/docs/integration/xmake/xmake.lua b/docs/mkdocs/docs/integration/xmake/xmake.lua new file mode 100644 index 0000000000..c5b332897d --- /dev/null +++ b/docs/mkdocs/docs/integration/xmake/xmake.lua @@ -0,0 +1,8 @@ +add_requires("nlohmann_json") + +add_rules("mode.debug", "mode.release") +target("xm") + set_kind("binary") + add_files("example.cpp") + add_packages("nlohmann_json") + set_languages("cxx11") diff --git a/docs/mkdocs/includes/glossary.md b/docs/mkdocs/includes/glossary.md new file mode 100644 index 0000000000..afc7ebc6a3 --- /dev/null +++ b/docs/mkdocs/includes/glossary.md @@ -0,0 +1,36 @@ + + +*[ADL]: Argument-dependent lookup +*[API]: Application Programming Interfaces +*[ASCII]: American Standard Code for Information Interchange +*[BDFL]: Benevolent Dictator for Life +*[BJData]: Binary JData +*[BSON]: Binary JSON +*[CBOR]: Concise Binary Object Representation +*[CC0]: Creative Commons Zero +*[CI]: Continuous Integration +*[DOM]: Document Object Model +*[EOF]: End of File +*[FAQ]: Frequently Asked Questions +*[GCC]: GNU Compiler Collection +*[HTTP]: Hypertext Transfer Protocol +*[ICC]: Intel C++ Compiler +*[IEEE]: Institute of Electrical and Electronics Engineers +*[ISO]: International Organization for Standardization +*[JSON]: JavaScript Object Notation +*[MIT]: Massachusetts Institute of Technology +*[MSVC]: Microsoft Visual C++ +*[MsgPack]: MessagePack +*[NASA]: National Aeronautics and Space Administration +*[NDK]: Native Development Kit +*[NaN]: Not a Number +*[RFC]: Request for Comments +*[RTTI]: Runtime Type Information +*[SAX]: Simple API for XML +*[SDK]: Software Development Kit +*[SFINAE]: Substitution failure is not an error +*[SHA]: Secure Hash Algorithm +*[SPDX]: Software Package Data Exchange +*[STL]: Standard Template Library +*[UBJSON]: Universal Binary JSON +*[UTF]: Unicode Transformation Format diff --git a/docs/mkdocs/mkdocs.yml b/docs/mkdocs/mkdocs.yml index 5e66db596f..ea089c7ea5 100644 --- a/docs/mkdocs/mkdocs.yml +++ b/docs/mkdocs/mkdocs.yml @@ -9,7 +9,7 @@ repo_url: https://github.com/nlohmann/json edit_uri: edit/develop/docs/mkdocs/docs # Copyright -copyright: Copyright © 2013 - 2023 Niels Lohmann +copyright: Copyright © 2013-2026 Niels Lohmann # Configuration theme: @@ -21,7 +21,7 @@ theme: primary: indigo accent: indigo toggle: - icon: material/brightness-7 + icon: material/brightness-7 name: Switch to dark mode - media: '(prefers-color-scheme: dark)' scheme: slate @@ -30,7 +30,8 @@ theme: toggle: icon: material/brightness-4 name: Switch to light mode - + icon: + repo: fontawesome/brands/github font: text: Roboto code: JetBrains Mono @@ -41,266 +42,268 @@ theme: - navigation.indexes - navigation.top - content.tabs.link + - content.action.edit + - content.action.view nav: - Home: - - index.md - - home/license.md - - "Code of Conduct": home/code_of_conduct.md - - "FAQ": home/faq.md - - home/exceptions.md - - home/releases.md - - home/design_goals.md - - home/sponsors.md + - index.md + - home/license.md + - "FAQ": home/faq.md + - home/exceptions.md + - home/releases.md + - home/design_goals.md + - home/architecture.md + - home/customers.md + - home/sponsors.md - Features: - - features/arbitrary_types.md - - Binary Formats: - - features/binary_formats/index.md - - features/binary_formats/bjdata.md - - features/binary_formats/bson.md - - features/binary_formats/cbor.md - - features/binary_formats/messagepack.md - - features/binary_formats/ubjson.md - - features/binary_values.md - - features/comments.md - - Element Access: - - features/element_access/index.md - - features/element_access/unchecked_access.md - - features/element_access/checked_access.md - - features/element_access/default_value.md - - features/iterators.md - - features/json_pointer.md - - features/json_patch.md - - features/merge_patch.md - - 'nlohmann Namespace': features/namespace.md - - features/object_order.md - - Parsing: - - features/parsing/index.md - - features/parsing/json_lines.md - - features/parsing/parse_exceptions.md - - features/parsing/parser_callbacks.md - - features/parsing/sax_interface.md - - features/assertions.md - - features/enum_conversion.md - - features/macros.md - - Types: - - features/types/index.md - - features/types/number_handling.md + - features/arbitrary_types.md + - Binary Formats: + - features/binary_formats/index.md + - features/binary_formats/bjdata.md + - features/binary_formats/bson.md + - features/binary_formats/cbor.md + - features/binary_formats/messagepack.md + - 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 + - features/element_access/checked_access.md + - features/element_access/default_value.md + - features/iterators.md + - features/json_pointer.md + - features/json_patch.md + - features/merge_patch.md + - features/modules.md + - 'nlohmann Namespace': features/namespace.md + - features/object_order.md + - Parsing: + - features/parsing/index.md + - features/parsing/json_lines.md + - features/parsing/parse_exceptions.md + - features/parsing/parser_callbacks.md + - features/parsing/sax_interface.md + - features/assertions.md + - features/enum_conversion.md + - features/macros.md + - Types: + - features/types/index.md + - features/types/number_handling.md - Integration: - - integration/index.md - - integration/migration_guide.md - - integration/cmake.md - - integration/package_managers.md - - integration/pkg-config.md + - integration/index.md + - integration/migration_guide.md + - integration/cmake.md + - integration/package_managers.md + - integration/pkg-config.md - API Documentation: - basic_json: - - 'Overview': api/basic_json/index.md - - '(Constructor)': api/basic_json/basic_json.md - - '(Destructor)': api/basic_json/~basic_json.md - - 'accept': api/basic_json/accept.md - - 'array': api/basic_json/array.md - - 'array_t': api/basic_json/array_t.md - - 'at': api/basic_json/at.md - - 'back': api/basic_json/back.md - - 'begin': api/basic_json/begin.md - - 'binary': api/basic_json/binary.md - - 'binary_t': api/basic_json/binary_t.md - - 'boolean_t': api/basic_json/boolean_t.md - - 'cbegin': api/basic_json/cbegin.md - - 'cbor_tag_handler_t': api/basic_json/cbor_tag_handler_t.md - - 'cend': api/basic_json/cend.md - - 'clear': api/basic_json/clear.md - - 'contains': api/basic_json/contains.md - - 'count': api/basic_json/count.md - - 'crbegin': api/basic_json/crbegin.md - - 'crend': api/basic_json/crend.md - - 'default_object_comparator_t': api/basic_json/default_object_comparator_t.md - - 'diff': api/basic_json/diff.md - - 'dump': api/basic_json/dump.md - - 'emplace': api/basic_json/emplace.md - - 'emplace_back': api/basic_json/emplace_back.md - - 'empty': api/basic_json/empty.md - - 'end': api/basic_json/end.md - - 'erase': api/basic_json/erase.md - - 'error_handler_t': api/basic_json/error_handler_t.md - - 'exception': api/basic_json/exception.md - - 'find': api/basic_json/find.md - - 'flatten': api/basic_json/flatten.md - - 'from_bjdata': api/basic_json/from_bjdata.md - - 'from_bson': api/basic_json/from_bson.md - - 'from_cbor': api/basic_json/from_cbor.md - - 'from_msgpack': api/basic_json/from_msgpack.md - - 'from_ubjson': api/basic_json/from_ubjson.md - - 'front': api/basic_json/front.md - - 'get': api/basic_json/get.md - - 'get_allocator': api/basic_json/get_allocator.md - - 'get_binary': api/basic_json/get_binary.md - - 'get_ptr': api/basic_json/get_ptr.md - - 'get_ref': api/basic_json/get_ref.md - - 'get_to': api/basic_json/get_to.md - - 'std::hash<basic_json>': api/basic_json/std_hash.md - - 'input_format_t': api/basic_json/input_format_t.md - - 'insert': api/basic_json/insert.md - - 'invalid_iterator': api/basic_json/invalid_iterator.md - - 'is_array': api/basic_json/is_array.md - - 'is_binary': api/basic_json/is_binary.md - - 'is_boolean': api/basic_json/is_boolean.md - - 'is_discarded': api/basic_json/is_discarded.md - - 'is_null': api/basic_json/is_null.md - - 'is_number': api/basic_json/is_number.md - - 'is_number_float': api/basic_json/is_number_float.md - - 'is_number_integer': api/basic_json/is_number_integer.md - - 'is_number_unsigned': api/basic_json/is_number_unsigned.md - - 'is_object': api/basic_json/is_object.md - - 'is_primitive': api/basic_json/is_primitive.md - - 'is_string': api/basic_json/is_string.md - - 'is_structured': api/basic_json/is_structured.md - - 'items': api/basic_json/items.md - - 'json_base_class_t': api/basic_json/json_base_class_t.md - - 'json_serializer': api/basic_json/json_serializer.md - - 'max_size': api/basic_json/max_size.md - - 'meta': api/basic_json/meta.md - - 'merge_patch': api/basic_json/merge_patch.md - - 'number_float_t': api/basic_json/number_float_t.md - - 'number_integer_t': api/basic_json/number_integer_t.md - - 'number_unsigned_t': api/basic_json/number_unsigned_t.md - - 'object': api/basic_json/object.md - - 'object_comparator_t': api/basic_json/object_comparator_t.md - - 'object_t': api/basic_json/object_t.md - - 'operator ValueType': api/basic_json/operator_ValueType.md - - 'operator value_t': api/basic_json/operator_value_t.md - - 'operator[]': api/basic_json/operator[].md - - 'operator=': api/basic_json/operator=.md - - 'operator+=': api/basic_json/operator+=.md - - 'operator==': api/basic_json/operator_eq.md - - 'operator!=': api/basic_json/operator_ne.md - - 'operator<': api/basic_json/operator_lt.md - - 'operator>': api/basic_json/operator_gt.md - - 'operator<=': api/basic_json/operator_le.md - - 'operator>=': api/basic_json/operator_ge.md - - 'operator<=>': api/basic_json/operator_spaceship.md - - 'out_of_range': api/basic_json/out_of_range.md - - 'other_error': api/basic_json/other_error.md - - 'parse': api/basic_json/parse.md - - 'parse_error': api/basic_json/parse_error.md - - 'parse_event_t': api/basic_json/parse_event_t.md - - 'parser_callback_t': api/basic_json/parser_callback_t.md - - 'patch': api/basic_json/patch.md - - 'patch_inplace': api/basic_json/patch_inplace.md - - 'push_back': api/basic_json/push_back.md - - 'rbegin': api/basic_json/rbegin.md - - 'rend': api/basic_json/rend.md - - 'sax_parse': api/basic_json/sax_parse.md - - 'size': api/basic_json/size.md - - 'string_t': api/basic_json/string_t.md - - 'swap': api/basic_json/swap.md - - 'std::swap<basic_json>': api/basic_json/std_swap.md - - 'to_bjdata': api/basic_json/to_bjdata.md - - 'to_bson': api/basic_json/to_bson.md - - 'to_cbor': api/basic_json/to_cbor.md - - 'to_msgpack': api/basic_json/to_msgpack.md - - 'to_string': api/basic_json/to_string.md - - 'to_ubjson': api/basic_json/to_ubjson.md - - 'type': api/basic_json/type.md - - 'type_error': api/basic_json/type_error.md - - 'type_name': api/basic_json/type_name.md - - 'unflatten': api/basic_json/unflatten.md - - 'update': api/basic_json/update.md - - 'value': api/basic_json/value.md - - 'value_t': api/basic_json/value_t.md + - 'Overview': api/basic_json/index.md + - '(Constructor)': api/basic_json/basic_json.md + - '(Destructor)': api/basic_json/~basic_json.md + - 'accept': api/basic_json/accept.md + - 'array': api/basic_json/array.md + - 'array_t': api/basic_json/array_t.md + - 'at': api/basic_json/at.md + - 'back': api/basic_json/back.md + - 'begin': api/basic_json/begin.md + - 'binary': api/basic_json/binary.md + - 'binary_t': api/basic_json/binary_t.md + - 'boolean_t': api/basic_json/boolean_t.md + - 'cbegin': api/basic_json/cbegin.md + - 'cbor_tag_handler_t': api/basic_json/cbor_tag_handler_t.md + - 'cend': api/basic_json/cend.md + - 'clear': api/basic_json/clear.md + - 'contains': api/basic_json/contains.md + - 'count': api/basic_json/count.md + - 'crbegin': api/basic_json/crbegin.md + - 'crend': api/basic_json/crend.md + - 'default_object_comparator_t': api/basic_json/default_object_comparator_t.md + - 'diff': api/basic_json/diff.md + - 'dump': api/basic_json/dump.md + - 'emplace': api/basic_json/emplace.md + - 'emplace_back': api/basic_json/emplace_back.md + - 'empty': api/basic_json/empty.md + - 'end': api/basic_json/end.md + - 'end_pos': api/basic_json/end_pos.md + - 'erase': api/basic_json/erase.md + - 'error_handler_t': api/basic_json/error_handler_t.md + - 'exception': api/basic_json/exception.md + - 'find': api/basic_json/find.md + - 'flatten': api/basic_json/flatten.md + - 'from_bjdata': api/basic_json/from_bjdata.md + - 'from_bson': api/basic_json/from_bson.md + - 'from_cbor': api/basic_json/from_cbor.md + - 'from_msgpack': api/basic_json/from_msgpack.md + - 'from_ubjson': api/basic_json/from_ubjson.md + - 'front': api/basic_json/front.md + - 'get': api/basic_json/get.md + - 'get_allocator': api/basic_json/get_allocator.md + - 'get_binary': api/basic_json/get_binary.md + - 'get_ptr': api/basic_json/get_ptr.md + - 'get_ref': api/basic_json/get_ref.md + - 'get_to': api/basic_json/get_to.md + - 'std::hash<basic_json>': api/basic_json/std_hash.md + - 'input_format_t': api/basic_json/input_format_t.md + - 'insert': api/basic_json/insert.md + - 'invalid_iterator': api/basic_json/invalid_iterator.md + - 'is_array': api/basic_json/is_array.md + - 'is_binary': api/basic_json/is_binary.md + - 'is_boolean': api/basic_json/is_boolean.md + - 'is_discarded': api/basic_json/is_discarded.md + - 'is_null': api/basic_json/is_null.md + - 'is_number': api/basic_json/is_number.md + - 'is_number_float': api/basic_json/is_number_float.md + - 'is_number_integer': api/basic_json/is_number_integer.md + - 'is_number_unsigned': api/basic_json/is_number_unsigned.md + - 'is_object': api/basic_json/is_object.md + - 'is_primitive': api/basic_json/is_primitive.md + - 'is_string': api/basic_json/is_string.md + - 'is_structured': api/basic_json/is_structured.md + - 'items': api/basic_json/items.md + - 'json_base_class_t': api/basic_json/json_base_class_t.md + - 'json_serializer': api/basic_json/json_serializer.md + - 'max_size': api/basic_json/max_size.md + - 'meta': api/basic_json/meta.md + - 'merge_patch': api/basic_json/merge_patch.md + - 'number_float_t': api/basic_json/number_float_t.md + - 'number_integer_t': api/basic_json/number_integer_t.md + - 'number_unsigned_t': api/basic_json/number_unsigned_t.md + - 'object': api/basic_json/object.md + - 'object_comparator_t': api/basic_json/object_comparator_t.md + - 'object_t': api/basic_json/object_t.md + - 'operator ValueType': api/basic_json/operator_ValueType.md + - 'operator value_t': api/basic_json/operator_value_t.md + - 'operator[]': api/basic_json/operator[].md + - 'operator=': api/basic_json/operator=.md + - 'operator+=': api/basic_json/operator+=.md + - 'operator==': api/basic_json/operator_eq.md + - 'operator!=': api/basic_json/operator_ne.md + - 'operator<': api/basic_json/operator_lt.md + - 'operator>': api/basic_json/operator_gt.md + - 'operator<=': api/basic_json/operator_le.md + - 'operator>=': api/basic_json/operator_ge.md + - 'operator<=>': api/basic_json/operator_spaceship.md + - 'out_of_range': api/basic_json/out_of_range.md + - 'other_error': api/basic_json/other_error.md + - 'parse': api/basic_json/parse.md + - 'parse_error': api/basic_json/parse_error.md + - 'parse_event_t': api/basic_json/parse_event_t.md + - 'parser_callback_t': api/basic_json/parser_callback_t.md + - 'patch': api/basic_json/patch.md + - 'patch_inplace': api/basic_json/patch_inplace.md + - 'push_back': api/basic_json/push_back.md + - 'rbegin': api/basic_json/rbegin.md + - 'rend': api/basic_json/rend.md + - 'sax_parse': api/basic_json/sax_parse.md + - 'size': api/basic_json/size.md + - 'start_pos': api/basic_json/start_pos.md + - 'string_t': api/basic_json/string_t.md + - 'swap': api/basic_json/swap.md + - 'std::swap<basic_json>': api/basic_json/std_swap.md + - 'to_bjdata': api/basic_json/to_bjdata.md + - 'to_bson': api/basic_json/to_bson.md + - 'to_cbor': api/basic_json/to_cbor.md + - 'to_msgpack': api/basic_json/to_msgpack.md + - 'to_string': api/basic_json/to_string.md + - 'to_ubjson': api/basic_json/to_ubjson.md + - 'type': api/basic_json/type.md + - 'type_error': api/basic_json/type_error.md + - 'type_name': api/basic_json/type_name.md + - 'unflatten': api/basic_json/unflatten.md + - 'update': api/basic_json/update.md + - 'value': api/basic_json/value.md + - 'value_t': api/basic_json/value_t.md - byte_container_with_subtype: - - 'Overview': api/byte_container_with_subtype/index.md - - '(constructor)': api/byte_container_with_subtype/byte_container_with_subtype.md - - 'clear_subtype': api/byte_container_with_subtype/clear_subtype.md - - 'has_subtype': api/byte_container_with_subtype/has_subtype.md - - 'set_subtype': api/byte_container_with_subtype/set_subtype.md - - 'subtype': api/byte_container_with_subtype/subtype.md + - 'Overview': api/byte_container_with_subtype/index.md + - '(constructor)': api/byte_container_with_subtype/byte_container_with_subtype.md + - 'clear_subtype': api/byte_container_with_subtype/clear_subtype.md + - 'has_subtype': api/byte_container_with_subtype/has_subtype.md + - 'set_subtype': api/byte_container_with_subtype/set_subtype.md + - 'subtype': api/byte_container_with_subtype/subtype.md - adl_serializer: - - 'Overview': api/adl_serializer/index.md - - 'from_json': api/adl_serializer/from_json.md - - 'to_json': api/adl_serializer/to_json.md + - 'Overview': api/adl_serializer/index.md + - 'from_json': api/adl_serializer/from_json.md + - 'to_json': api/adl_serializer/to_json.md - 'json': api/json.md - json_pointer: - - 'Overview': api/json_pointer/index.md - - '(Constructor)': api/json_pointer/json_pointer.md - - 'back': api/json_pointer/back.md - - 'empty': api/json_pointer/empty.md - - 'operator string_t': api/json_pointer/operator_string_t.md - - 'operator==': api/json_pointer/operator_eq.md - - 'operator!=': api/json_pointer/operator_ne.md - - 'operator/': api/json_pointer/operator_slash.md - - 'operator/=': api/json_pointer/operator_slasheq.md - - 'parent_pointer': api/json_pointer/parent_pointer.md - - 'pop_back': api/json_pointer/pop_back.md - - 'push_back': api/json_pointer/push_back.md - - 'string_t': api/json_pointer/string_t.md - - 'to_string': api/json_pointer/to_string.md + - 'Overview': api/json_pointer/index.md + - '(Constructor)': api/json_pointer/json_pointer.md + - 'back': api/json_pointer/back.md + - 'empty': api/json_pointer/empty.md + - 'operator string_t': api/json_pointer/operator_string_t.md + - 'operator==': api/json_pointer/operator_eq.md + - 'operator!=': api/json_pointer/operator_ne.md + - 'operator/': api/json_pointer/operator_slash.md + - 'operator/=': api/json_pointer/operator_slasheq.md + - 'parent_pointer': api/json_pointer/parent_pointer.md + - 'pop_back': api/json_pointer/pop_back.md + - 'push_back': api/json_pointer/push_back.md + - 'string_t': api/json_pointer/string_t.md + - 'to_string': api/json_pointer/to_string.md - json_sax: - - 'Overview': api/json_sax/index.md - - 'binary': api/json_sax/binary.md - - 'boolean': api/json_sax/boolean.md - - 'end_array': api/json_sax/end_array.md - - 'end_object': api/json_sax/end_object.md - - 'key': api/json_sax/key.md - - 'null': api/json_sax/null.md - - 'number_float': api/json_sax/number_float.md - - 'number_integer': api/json_sax/number_integer.md - - 'number_unsigned': api/json_sax/number_unsigned.md - - 'parse_error': api/json_sax/parse_error.md - - 'start_array': api/json_sax/start_array.md - - 'start_object': api/json_sax/start_object.md - - 'string': api/json_sax/string.md - - 'operator<<(basic_json)': api/operator_ltlt.md - - 'operator<<(json_pointer)': api/operator_ltlt.md + - 'Overview': api/json_sax/index.md + - 'binary': api/json_sax/binary.md + - 'boolean': api/json_sax/boolean.md + - 'end_array': api/json_sax/end_array.md + - 'end_object': api/json_sax/end_object.md + - 'key': api/json_sax/key.md + - 'null': api/json_sax/null.md + - 'number_float': api/json_sax/number_float.md + - 'number_integer': api/json_sax/number_integer.md + - 'number_unsigned': api/json_sax/number_unsigned.md + - 'parse_error': api/json_sax/parse_error.md + - 'start_array': api/json_sax/start_array.md + - 'start_object': api/json_sax/start_object.md + - 'string': api/json_sax/string.md + - 'operator<<(basic_json), operator<<(json_pointer)': api/operator_ltlt.md - 'operator>>(basic_json)': api/operator_gtgt.md - 'operator""_json': api/operator_literal_json.md - 'operator""_json_pointer': api/operator_literal_json_pointer.md - 'ordered_json': api/ordered_json.md - 'ordered_map': api/ordered_map.md - macros: - - 'Overview': api/macros/index.md - - 'JSON_ASSERT': api/macros/json_assert.md - - 'JSON_CATCH_USER': api/macros/json_throw_user.md - - 'JSON_DIAGNOSTICS': api/macros/json_diagnostics.md - - 'JSON_DISABLE_ENUM_SERIALIZATION': api/macros/json_disable_enum_serialization.md - - 'JSON_HAS_CPP_11': api/macros/json_has_cpp_11.md - - 'JSON_HAS_CPP_14': api/macros/json_has_cpp_11.md - - 'JSON_HAS_CPP_17': api/macros/json_has_cpp_11.md - - 'JSON_HAS_CPP_20': api/macros/json_has_cpp_11.md - - 'JSON_HAS_EXPERIMENTAL_FILESYSTEM': api/macros/json_has_filesystem.md - - 'JSON_HAS_FILESYSTEM': api/macros/json_has_filesystem.md - - 'JSON_HAS_RANGES': api/macros/json_has_ranges.md - - 'JSON_HAS_STATIC_RTTI': api/macros/json_has_static_rtti.md - - 'JSON_HAS_THREE_WAY_COMPARISON': api/macros/json_has_three_way_comparison.md - - 'JSON_NOEXCEPTION': api/macros/json_noexception.md - - 'JSON_NO_IO': api/macros/json_no_io.md - - 'JSON_SKIP_LIBRARY_VERSION_CHECK': api/macros/json_skip_library_version_check.md - - 'JSON_SKIP_UNSUPPORTED_COMPILER_CHECK': api/macros/json_skip_unsupported_compiler_check.md - - 'JSON_THROW_USER': api/macros/json_throw_user.md - - 'JSON_TRY_USER': api/macros/json_throw_user.md - - 'JSON_USE_GLOBAL_UDLS': api/macros/json_use_global_udls.md - - 'JSON_USE_IMPLICIT_CONVERSIONS': api/macros/json_use_implicit_conversions.md - - 'JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON': api/macros/json_use_legacy_discarded_value_comparison.md - - 'NLOHMANN_DEFINE_TYPE_INTRUSIVE': api/macros/nlohmann_define_type_intrusive.md - - 'NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT': api/macros/nlohmann_define_type_intrusive.md - - 'NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE': api/macros/nlohmann_define_type_non_intrusive.md - - 'NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT': api/macros/nlohmann_define_type_non_intrusive.md - - 'NLOHMANN_JSON_NAMESPACE': api/macros/nlohmann_json_namespace.md - - 'NLOHMANN_JSON_NAMESPACE_BEGIN': api/macros/nlohmann_json_namespace_begin.md - - 'NLOHMANN_JSON_NAMESPACE_END': api/macros/nlohmann_json_namespace_begin.md - - 'NLOHMANN_JSON_NAMESPACE_NO_VERSION': api/macros/nlohmann_json_namespace_no_version.md - - 'NLOHMANN_JSON_SERIALIZE_ENUM': api/macros/nlohmann_json_serialize_enum.md - - 'NLOHMANN_JSON_VERSION_MAJOR': api/macros/nlohmann_json_version_major.md - - 'NLOHMANN_JSON_VERSION_MINOR': api/macros/nlohmann_json_version_major.md - - 'NLOHMANN_JSON_VERSION_PATCH': api/macros/nlohmann_json_version_major.md + - 'Overview': api/macros/index.md + - 'JSON_ASSERT': api/macros/json_assert.md + - 'JSON_CATCH_USER, JSON_THROW_USER, JSON_TRY_USER': api/macros/json_throw_user.md + - 'JSON_DIAGNOSTICS': api/macros/json_diagnostics.md + - 'JSON_DIAGNOSTIC_POSITIONS': api/macros/json_diagnostic_positions.md + - 'JSON_DISABLE_ENUM_SERIALIZATION': api/macros/json_disable_enum_serialization.md + - 'JSON_HAS_CPP_11, JSON_HAS_CPP_14, JSON_HAS_CPP_17, JSON_HAS_CPP_20': api/macros/json_has_cpp_11.md + - 'JSON_HAS_EXPERIMENTAL_FILESYSTEM, JSON_HAS_FILESYSTEM': api/macros/json_has_filesystem.md + - 'JSON_HAS_RANGES': api/macros/json_has_ranges.md + - 'JSON_HAS_STATIC_RTTI': api/macros/json_has_static_rtti.md + - 'JSON_HAS_THREE_WAY_COMPARISON': api/macros/json_has_three_way_comparison.md + - 'JSON_NOEXCEPTION': api/macros/json_noexception.md + - 'JSON_NO_IO': api/macros/json_no_io.md + - 'JSON_SKIP_LIBRARY_VERSION_CHECK': api/macros/json_skip_library_version_check.md + - 'JSON_SKIP_UNSUPPORTED_COMPILER_CHECK': api/macros/json_skip_unsupported_compiler_check.md + - 'JSON_USE_GLOBAL_UDLS': api/macros/json_use_global_udls.md + - 'JSON_USE_IMPLICIT_CONVERSIONS': api/macros/json_use_implicit_conversions.md + - 'JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON': api/macros/json_use_legacy_discarded_value_comparison.md + - 'NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE, NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_derived_type.md + - 'NLOHMANN_DEFINE_TYPE_INTRUSIVE, NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_type_intrusive.md + - 'NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE, NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_type_non_intrusive.md + - 'NLOHMANN_JSON_NAMESPACE': api/macros/nlohmann_json_namespace.md + - 'NLOHMANN_JSON_NAMESPACE_BEGIN, NLOHMANN_JSON_NAMESPACE_END': api/macros/nlohmann_json_namespace_begin.md + - 'NLOHMANN_JSON_NAMESPACE_NO_VERSION': api/macros/nlohmann_json_namespace_no_version.md + - 'NLOHMANN_JSON_SERIALIZE_ENUM': api/macros/nlohmann_json_serialize_enum.md + - 'NLOHMANN_JSON_VERSION_MAJOR, NLOHMANN_JSON_VERSION_MINOR, NLOHMANN_JSON_VERSION_PATCH': api/macros/nlohmann_json_version_major.md + - Community: + - community/index.md + - "Code of Conduct": community/code_of_conduct.md + - community/contribution_guidelines.md + - community/quality_assurance.md + - community/governance.md + - community/security_policy.md # Extras extra: social: - icon: fontawesome/brands/github link: https://github.com/nlohmann - - icon: fontawesome/brands/twitter - link: https://twitter.com/nlohmann - icon: fontawesome/brands/linkedin link: https://www.linkedin.com/in/nielslohmann/ - icon: fontawesome/brands/xing @@ -319,6 +322,7 @@ markdown_extensions: guess_lang: false - toc: permalink: true + - md_in_html - pymdownx.arithmatex - pymdownx.betterem: smart_enable: all @@ -332,7 +336,11 @@ markdown_extensions: - pymdownx.magiclink - pymdownx.mark #- pymdownx.smartsymbols - - pymdownx.superfences + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format - pymdownx.tasklist: custom_checkbox: true - pymdownx.tabbed: @@ -341,26 +349,35 @@ markdown_extensions: - pymdownx.snippets: base_path: docs check_paths: true - - plantuml_markdown: - format: svg + restrict_base_path: false # needed to allow including files from the .github folder + auto_append: + - ../includes/glossary.md plugins: - - search: - separator: '[\s\-\.]' - lang: en - - minify: - minify_html: true - - git-revision-date-localized - - redirects: - redirect_maps: - 'api/basic_json/operator_gtgt.md': api/operator_gtgt.md - 'api/basic_json/operator_ltlt.md': api/operator_ltlt.md - 'api/basic_json/operator_literal_json.md': api/operator_literal_json.md - 'api/basic_json/operator_literal_json_pointer.md': api/operator_literal_json_pointer.md - 'api/json_pointer/operator_string.md': api/json_pointer/operator_string_t.md + - search: + separator: '[\s\-\.]' + lang: en + - minify: + minify_html: true + - git-revision-date-localized + - redirects: + redirect_maps: + 'api/basic_json/operator_gtgt.md': api/operator_gtgt.md + 'api/basic_json/operator_ltlt.md': api/operator_ltlt.md + 'api/basic_json/operator_literal_json.md': api/operator_literal_json.md + 'api/basic_json/operator_literal_json_pointer.md': api/operator_literal_json_pointer.md + 'api/json_pointer/operator_string.md': api/json_pointer/operator_string_t.md + 'home/code_of_conduct.md': community/code_of_conduct.md + - htmlproofer: # see https://github.com/manuzhang/mkdocs-htmlproofer-plugin + enabled: !ENV [ENABLED_HTMLPROOFER, False] + ignore_urls: + - http://nlohmann.github.io/json/* + - https://nlohmann.github.io/json/* + - mailto:* + - privacy extra_css: - - css/custom.css + - css/custom.css extra_javascript: - https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-MML-AM_CHTML diff --git a/docs/mkdocs/requirements.txt b/docs/mkdocs/requirements.txt index b397d545d0..291fc22c1f 100644 --- a/docs/mkdocs/requirements.txt +++ b/docs/mkdocs/requirements.txt @@ -1,49 +1,11 @@ -Babel==2.13.1 -certifi==2023.7.22 -charset-normalizer==3.3.1 -click==8.1.7 -csscompressor==0.9.5 -future==0.18.3 -ghp-import==2.1.0 -gitdb==4.0.11 -GitPython==3.1.40 -htmlmin==0.1.12 -httplib2==0.22.0 -idna==3.4 -importlib-metadata==6.8.0 -Jinja2==3.1.2 -joblib==1.3.2 -jsmin==3.0.1 -livereload==2.6.3 -lunr==0.7.0.post1 -Markdown==3.5 -markdown-include==0.8.1 -MarkupSafe==2.1.3 -mergedeep==1.3.4 -mkdocs==1.5.3 -mkdocs-git-revision-date-localized-plugin==1.2.1 -mkdocs-material==9.4.7 -mkdocs-material-extensions==1.3 -mkdocs-minify-plugin==0.7.1 -mkdocs-redirects==1.2.1 -mkdocs-simple-hooks==0.1.5 -nltk==3.8.1 -packaging==23.2 -plantuml==0.3.0 -plantuml-markdown==3.9.2 -Pygments==2.16.1 -pymdown-extensions==10.3.1 -pyparsing==3.1.1 -python-dateutil==2.8.2 -pytz==2023.3.post1 -PyYAML==6.0.1 -pyyaml_env_tag==0.1 -regex==2023.10.3 -requests==2.31.0 -six==1.16.0 -smmap==5.0.1 -tornado==6.3.3 -tqdm==4.66.1 -urllib3==2.0.7 -watchdog==3.0.0 -zipp==3.17.0 +wheel==0.45.1 + +mkdocs==1.6.1 # documentation framework +mkdocs-git-revision-date-localized-plugin==1.5.0 # plugin "git-revision-date-localized" +mkdocs-material==9.7.1 # theme for mkdocs +mkdocs-material-extensions==1.3.1 # extensions +mkdocs-minify-plugin==0.8.0 # plugin "minify" +mkdocs-redirects==1.2.2 # plugin "redirects" +mkdocs-htmlproofer-plugin==1.4.0 # plugin "htmlproofer" + +PyYAML==6.0.3 # linter diff --git a/docs/mkdocs/scripts/check_structure.py b/docs/mkdocs/scripts/check_structure.py index 643482af24..c8d637d06b 100755 --- a/docs/mkdocs/scripts/check_structure.py +++ b/docs/mkdocs/scripts/check_structure.py @@ -5,47 +5,49 @@ import re import sys +import yaml + warnings = 0 -def report(rule, location, description): +def report(rule, location, description) -> None: global warnings warnings += 1 print(f'{warnings:3}. {location}: {description} [{rule}]') -def check_structure(): +def check_structure() -> None: expected_sections = [ - 'Template parameters', - 'Specializations', - 'Iterator invalidation', - 'Requirements', - 'Member types', - 'Member functions', - 'Member variables', - 'Static functions', - 'Non-member functions', - 'Literals', - 'Helper classes', - 'Parameters', - 'Return value', - 'Exception safety', - 'Exceptions', - 'Complexity', - 'Possible implementation', - 'Default definition', - 'Notes', - 'Examples', - 'See also', - 'Version history' + "Template parameters", + "Specializations", + "Iterator invalidation", + "Requirements", + "Member types", + "Member functions", + "Member variables", + "Static functions", + "Non-member functions", + "Literals", + "Helper classes", + "Parameters", + "Return value", + "Exception safety", + "Exceptions", + "Complexity", + "Possible implementation", + "Default definition", + "Notes", + "Examples", + "See also", + "Version history", ] required_sections = [ - 'Examples', - 'Version history' + "Examples", + "Version history", ] - files = sorted(glob.glob('api/**/*.md', recursive=True)) + files = sorted(glob.glob("api/**/*.md", recursive=True)) for file in files: with open(file) as file_content: section_idx = -1 # the index of the current h2 section @@ -60,30 +62,30 @@ def check_structure(): for lineno, original_line in enumerate(file_content.readlines()): line = original_line.strip() - if line.startswith('# '): + if line.startswith("# "): h1sections += 1 # there should only be one top-level title if h1sections > 1: - report('structure/unexpected_section', f'{file}:{lineno+1}', f'unexpected top-level title "{line}"') + report("structure/unexpected_section", f"{file}:{lineno+1}", f'unexpected top-level title "{line}"') h1sections = 1 # Overview pages should have a better title - if line == '# Overview': - report('style/title', f'{file}:{lineno+1}', 'overview pages should have a better title than "Overview"') + if line == "# Overview": + report("style/title", f"{file}:{lineno+1}", 'overview pages should have a better title than "Overview"') # lines longer than 160 characters are bad (unless they are tables) - if len(line) > 160 and '|' not in line: - report('whitespace/line_length', f'{file}:{lineno+1} ({current_section})', f'line is too long ({len(line)} vs. 160 chars)') + if len(line) > 160 and "|" not in line: + report("whitespace/line_length", f"{file}:{lineno+1} ({current_section})", f"line is too long ({len(line)} vs. 160 chars)") # sections in `` comments are treated as present - if line.startswith('') + if line.startswith("") existing_sections.append(current_section) # check if sections are correct - if line.startswith('## '): + if line.startswith("## "): # before starting a new section, check if the previous one documented all overloads if current_section in documented_overloads and last_overload != 0: if len(documented_overloads[current_section]) > 0 and len(documented_overloads[current_section]) != last_overload: @@ -91,21 +93,20 @@ def check_structure(): undocumented = [x for x in expected if x not in documented_overloads[current_section]] unexpected = [x for x in documented_overloads[current_section] if x not in expected] if len(undocumented): - report('style/numbering', f'{file}:{lineno} ({current_section})', f'undocumented overloads: {", ".join([f"({x})" for x in undocumented])}') + report("style/numbering", f"{file}:{lineno} ({current_section})", f'undocumented overloads: {", ".join([f"({x})" for x in undocumented])}') if len(unexpected): - report('style/numbering', f'{file}:{lineno} ({current_section})', f'unexpected overloads: {", ".join([f"({x})" for x in unexpected])}') + report("style/numbering", f"{file}:{lineno} ({current_section})", f'unexpected overloads: {", ".join([f"({x})" for x in unexpected])}') - current_section = line.strip('## ') + current_section = line.strip("## ") existing_sections.append(current_section) if current_section in expected_sections: idx = expected_sections.index(current_section) if idx <= section_idx: - report('structure/section_order', f'{file}:{lineno+1}', f'section "{current_section}" is in an unexpected order (should be before "{expected_sections[section_idx]}")') + report("structure/section_order", f"{file}:{lineno+1}", f'section "{current_section}" is in an unexpected order (should be before "{expected_sections[section_idx]}")') section_idx = idx - else: - if 'index.md' not in file: # index.md files may have a different structure - report('structure/unknown_section', f'{file}:{lineno+1}', f'section "{current_section}" is not part of the expected sections') + elif "index.md" not in file: # index.md files may have a different structure + report("structure/unknown_section", f"{file}:{lineno+1}", f'section "{current_section}" is not part of the expected sections') # collect the numbered items of the current section to later check if they match the number of overloads if last_overload != 0 and not in_initial_code_example: @@ -116,64 +117,106 @@ def check_structure(): documented_overloads[current_section].append(number) # code example - if line == '```cpp' and section_idx == -1: + if line == "```cpp" and section_idx == -1: in_initial_code_example = True - if in_initial_code_example and line.startswith('//') and line not in ['// since C++20', '// until C++20']: + if in_initial_code_example and line.startswith("//") and line not in ["// since C++20", "// until C++20"]: # check numbering of overloads if any(map(str.isdigit, line)): - number = int(re.findall(r'\d+', line)[0]) + number = int(re.findall(r"\d+", line)[0]) if number != last_overload + 1: - report('style/numbering', f'{file}:{lineno+1}', f'expected number ({number}) to be ({last_overload +1 })') + report("style/numbering", f"{file}:{lineno+1}", f"expected number ({number}) to be ({last_overload +1 })") last_overload = number - if any(map(str.isdigit, line)) and '(' not in line: - report('style/numbering', f'{file}:{lineno+1}', f'number should be in parentheses: {line}') + if any(map(str.isdigit, line)) and "(" not in line: + report("style/numbering", f"{file}:{lineno+1}", f"number should be in parentheses: {line}") - if line == '```' and in_initial_code_example: + if line == "```" and in_initial_code_example: in_initial_code_example = False # consecutive blank lines are bad - if line == '' and previous_line == '': - report('whitespace/blank_lines', f'{file}:{lineno}-{lineno+1} ({current_section})', 'consecutive blank lines') + if line == "" and previous_line == "": + report("whitespace/blank_lines", f"{file}:{lineno}-{lineno+1} ({current_section})", "consecutive blank lines") # check that non-example admonitions have titles - untitled_admonition = re.match(r'^(\?\?\?|!!!) ([^ ]+)$', line) - if untitled_admonition and untitled_admonition.group(2) != 'example': - report('style/admonition_title', f'{file}:{lineno} ({current_section})', f'"{untitled_admonition.group(2)}" admonitions should have a title') + untitled_admonition = re.match(r"^(\?\?\?|!!!) ([^ ]+)$", line) + if untitled_admonition and untitled_admonition.group(2) != "example": + report("style/admonition_title", f"{file}:{lineno} ({current_section})", f'"{untitled_admonition.group(2)}" admonitions should have a title') previous_line = line - if 'index.md' not in file: # index.md files may have a different structure + if "index.md" not in file: # index.md files may have a different structure for required_section in required_sections: if required_section not in existing_sections: - report('structure/missing_section', f'{file}:{lineno+1}', f'required section "{required_section}" was not found') + report("structure/missing_section", f"{file}:{lineno+1}", f'required section "{required_section}" was not found') -def check_examples(): - example_files = sorted(glob.glob('../../examples/*.cpp')) - markdown_files = sorted(glob.glob('**/*.md', recursive=True)) +def check_examples() -> None: + example_files = sorted(glob.glob("../../examples/*.cpp")) + markdown_files = sorted(glob.glob("**/*.md", recursive=True)) # check if every example file is used in at least one markdown file for example_file in example_files: - example_file = os.path.join('examples', os.path.basename(example_file)) + example_file = os.path.join("examples", os.path.basename(example_file)) found = False for markdown_file in markdown_files: - content = ' '.join(open(markdown_file).readlines()) + content = " ".join(open(markdown_file).readlines()) if example_file in content: found = True break if not found: - report('examples/missing', f'{example_file}', 'example file is not used in any documentation file') + report("examples/missing", f"{example_file}", "example file is not used in any documentation file") + + +def check_links() -> None: + """Check that every entry in the navigation (nav in mkdocs.yml) links to at most one file. If a file is linked more + than once, then the first entry is repeated. See https://github.com/nlohmann/json/issues/4564 for the issue in + this project and https://github.com/mkdocs/mkdocs/issues/3428 for the root cause. + + The issue can be fixed by merging the keys, so + + - 'NLOHMANN_JSON_VERSION_MAJOR': api/macros/nlohmann_json_version_major.md + - 'NLOHMANN_JSON_VERSION_MINOR': api/macros/nlohmann_json_version_major.md + + would be replaced with + + - 'NLOHMANN_JSON_VERSION_MAJOR, NLOHMANN_JSON_VERSION_MINOR': api/macros/nlohmann_json_version_major.md + """ + file_with_path = {} + + def collect_links(node, path="") -> None: + if isinstance(node, list): + for x in node: + collect_links(x, path) + elif isinstance(node, dict): + for p, x in node.items(): + collect_links(x, path + "/" + p) + else: + if node not in file_with_path: + file_with_path[node] = [] + file_with_path[node].append(path) + + with open("../mkdocs.yml") as mkdocs_file: + # see https://github.com/yaml/pyyaml/issues/86#issuecomment-1042485535 + yaml.add_multi_constructor("tag:yaml.org,2002:python/name", lambda loader, suffix, node: None, Loader=yaml.SafeLoader) + yaml.add_multi_constructor("!ENV", lambda loader, suffix, node: None, Loader=yaml.SafeLoader) + y = yaml.safe_load(mkdocs_file) + + collect_links(y["nav"]) + for duplicate_file in [x for x in file_with_path if len(file_with_path[x]) > 1]: + file_list = [f'"{x}"' for x in file_with_path[duplicate_file]] + file_list_str = ", ".join(file_list) + report("nav/duplicate_files", "mkdocs.yml", f'file "{duplicate_file}" is linked with multiple keys in "nav": {file_list_str}; only one is rendered properly, see #4564') -if __name__ == '__main__': - print(120 * '-') +if __name__ == "__main__": + print(120 * "-") check_structure() check_examples() - print(120 * '-') + check_links() + print(120 * "-") if warnings > 0: sys.exit(1) diff --git a/docs/usages/ios.png b/docs/usages/ios.png deleted file mode 100755 index 1d2c1b8071..0000000000 Binary files a/docs/usages/ios.png and /dev/null differ diff --git a/docs/usages/macos.png b/docs/usages/macos.png deleted file mode 100644 index 107b5f07fd..0000000000 Binary files a/docs/usages/macos.png and /dev/null differ diff --git a/include/nlohmann/adl_serializer.hpp b/include/nlohmann/adl_serializer.hpp index 56a606c0f6..ada1e3200c 100644 --- a/include/nlohmann/adl_serializer.hpp +++ b/include/nlohmann/adl_serializer.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once diff --git a/include/nlohmann/byte_container_with_subtype.hpp b/include/nlohmann/byte_container_with_subtype.hpp index 91382cd682..4e79d9fa75 100644 --- a/include/nlohmann/byte_container_with_subtype.hpp +++ b/include/nlohmann/byte_container_with_subtype.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once diff --git a/include/nlohmann/detail/abi_macros.hpp b/include/nlohmann/detail/abi_macros.hpp index f48b9eb1d5..3e07a6a984 100644 --- a/include/nlohmann/detail/abi_macros.hpp +++ b/include/nlohmann/detail/abi_macros.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once @@ -12,20 +12,24 @@ #ifndef JSON_SKIP_LIBRARY_VERSION_CHECK #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) - #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 3 + #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 12 || NLOHMANN_JSON_VERSION_PATCH != 0 #warning "Already included a different version of the library!" #endif #endif #endif #define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) -#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum) -#define NLOHMANN_JSON_VERSION_PATCH 3 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_MINOR 12 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_PATCH 0 // NOLINT(modernize-macro-to-enum) #ifndef JSON_DIAGNOSTICS #define JSON_DIAGNOSTICS 0 #endif +#ifndef JSON_DIAGNOSTIC_POSITIONS + #define JSON_DIAGNOSTIC_POSITIONS 0 +#endif + #ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 #endif @@ -36,6 +40,12 @@ #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS #endif +#if JSON_DIAGNOSTIC_POSITIONS + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS _dp +#else + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS +#endif + #if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp #else @@ -47,14 +57,15 @@ #endif // Construct the namespace ABI tags component -#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b -#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \ - NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) +#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) json_abi ## a ## b ## c +#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c) \ + NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) #define NLOHMANN_JSON_ABI_TAGS \ NLOHMANN_JSON_ABI_TAGS_CONCAT( \ NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ - NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON) + NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \ + NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS) // Construct the namespace version component #define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index aa2f0cbf4c..872435f9d9 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once @@ -29,6 +29,15 @@ #include #include +// include after macro_scope.hpp +#ifdef JSON_HAS_CPP_17 + #include // optional +#endif + +#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM + #include // u8string_view +#endif + NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { @@ -43,6 +52,21 @@ inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n) n = nullptr; } +#ifdef JSON_HAS_CPP_17 +template +void from_json(const BasicJsonType& j, std::optional& opt) +{ + if (j.is_null()) + { + opt = std::nullopt; + } + else + { + opt.emplace(j.template get()); + } +} +#endif // JSON_HAS_CPP_17 + // overloads for basic_json template parameters template < typename BasicJsonType, typename ArithmeticType, enable_if_t < std::is_arithmetic::value&& @@ -190,6 +214,54 @@ auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines } } +template +auto from_json(const BasicJsonType& j, T (&arr)[N1][N2]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +-> decltype(j.template get(), void()) +{ + for (std::size_t i1 = 0; i1 < N1; ++i1) + { + for (std::size_t i2 = 0; i2 < N2; ++i2) + { + arr[i1][i2] = j.at(i1).at(i2).template get(); + } + } +} + +template +auto from_json(const BasicJsonType& j, T (&arr)[N1][N2][N3]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +-> decltype(j.template get(), void()) +{ + for (std::size_t i1 = 0; i1 < N1; ++i1) + { + for (std::size_t i2 = 0; i2 < N2; ++i2) + { + for (std::size_t i3 = 0; i3 < N3; ++i3) + { + arr[i1][i2][i3] = j.at(i1).at(i2).at(i3).template get(); + } + } + } +} + +template +auto from_json(const BasicJsonType& j, T (&arr)[N1][N2][N3][N4]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +-> decltype(j.template get(), void()) +{ + for (std::size_t i1 = 0; i1 < N1; ++i1) + { + for (std::size_t i2 = 0; i2 < N2; ++i2) + { + for (std::size_t i3 = 0; i3 < N3; ++i3) + { + for (std::size_t i4 = 0; i4 < N4; ++i4) + { + arr[i1][i2][i3][i4] = j.at(i1).at(i2).at(i3).at(i4).template get(); + } + } + } + } +} + template inline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/) { @@ -275,7 +347,7 @@ void()) template < typename BasicJsonType, typename T, std::size_t... Idx > std::array from_json_inplace_array_impl(BasicJsonType&& j, - identity_tag> /*unused*/, index_sequence /*unused*/) + identity_tag> /*unused*/, index_sequence /*unused*/) { return { { std::forward(j).at(Idx).template get()... } }; } @@ -326,7 +398,7 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) } // overload for arithmetic types, not chosen for basic_json template arguments -// (BooleanType, etc..); note: Is it really necessary to provide explicit +// (BooleanType, etc.); note: Is it really necessary to provide explicit // overloads for boolean_t etc. in case of a custom BooleanType which is not // an arithmetic type? template < typename BasicJsonType, typename ArithmeticType, @@ -379,6 +451,12 @@ std::tuple from_json_tuple_impl_base(BasicJsonType&& j, index_sequence< return std::make_tuple(std::forward(j).at(Idx).template get()...); } +template +std::tuple<> from_json_tuple_impl_base(BasicJsonType& /*unused*/, index_sequence<> /*unused*/) +{ + return {}; +} + template < typename BasicJsonType, class A1, class A2 > std::pair from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<0> /*unused*/) { @@ -464,7 +542,15 @@ inline void from_json(const BasicJsonType& j, std_fs::path& p) { JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j)); } - p = *j.template get_ptr(); + const auto& s = *j.template get_ptr(); + // Checking for C++20 standard or later can be insufficient in case the + // library support for char8_t is either incomplete or was disabled + // altogether. Use the __cpp_lib_char8_t feature test instead. +#if defined(__cpp_lib_char8_t) && (__cpp_lib_char8_t >= 201907L) + p = std_fs::path(std::u8string_view(reinterpret_cast(s.data()), s.size())); +#else + p = std_fs::u8path(s); // accepts UTF-8 encoded std::string in C++17, deprecated in C++20 +#endif } #endif diff --git a/include/nlohmann/detail/conversions/to_chars.hpp b/include/nlohmann/detail/conversions/to_chars.hpp index e10741c923..70fb9b933e 100644 --- a/include/nlohmann/detail/conversions/to_chars.hpp +++ b/include/nlohmann/detail/conversions/to_chars.hpp @@ -1,10 +1,10 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // // SPDX-FileCopyrightText: 2009 Florian Loitsch -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once @@ -130,7 +130,7 @@ struct diyfp // f * 2^e // p_lo = p0_lo + (Q << 32) // // But in this particular case here, the full p_lo is not required. - // Effectively we only need to add the highest bit in p_lo to p_hi (and + // Effectively, we only need to add the highest bit in p_lo to p_hi (and // Q_hi + 1 does not overflow). Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up @@ -220,7 +220,7 @@ boundaries compute_boundaries(FloatType value) // Compute the boundaries m- and m+ of the floating-point value // v = f * 2^e. // - // Determine v- and v+, the floating-point predecessor and successor if v, + // Determine v- and v+, the floating-point predecessor and successor of v, // respectively. // // v- = v - 2^e if f != 2^(p-1) or e == e_min (A) @@ -239,10 +239,10 @@ boundaries compute_boundaries(FloatType value) // v- m- v m+ v+ const bool lower_boundary_is_closer = F == 0 && E > 1; - const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); + const diyfp m_plus = diyfp((2 * v.f) + 1, v.e - 1); const diyfp m_minus = lower_boundary_is_closer - ? diyfp(4 * v.f - 1, v.e - 2) // (B) - : diyfp(2 * v.f - 1, v.e - 1); // (A) + ? diyfp((4 * v.f) - 1, v.e - 2) // (B) + : diyfp((2 * v.f) - 1, v.e - 1); // (A) // Determine the normalized w+ = m+. const diyfp w_plus = diyfp::normalize(m_plus); @@ -375,7 +375,7 @@ inline cached_power get_cached_power_for_binary_exponent(int e) // (A smaller distance gamma-alpha would require a larger table.) // NB: - // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34. + // Actually, this function returns c, such that -60 <= e_c + e + 64 <= -34. constexpr int kCachedPowersMinDecExp = -300; constexpr int kCachedPowersDecStep = 8; @@ -472,7 +472,7 @@ inline cached_power get_cached_power_for_binary_exponent(int e) JSON_ASSERT(e >= -1500); JSON_ASSERT(e <= 1500); const int f = kAlpha - e - 1; - const int k = (f * 78913) / (1 << 18) + static_cast(f > 0); + const int k = ((f * 78913) / (1 << 18)) + static_cast(f > 0); const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; JSON_ASSERT(index >= 0); @@ -687,8 +687,8 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, decimal_exponent += n; - // We may now just stop. But instead look if the buffer could be - // decremented to bring V closer to w. + // We may now just stop. But instead, it looks as if the buffer + // could be decremented to bring V closer to w. // // pow10 = 10^n is now 1 ulp in the decimal representation V. // The rounding procedure works with diyfp's with an implicit @@ -950,15 +950,15 @@ inline char* append_exponent(char* buf, int e) } else if (k < 100) { - *buf++ = static_cast('0' + k / 10); + *buf++ = static_cast('0' + (k / 10)); k %= 10; *buf++ = static_cast('0' + k); } else { - *buf++ = static_cast('0' + k / 100); + *buf++ = static_cast('0' + (k / 100)); k %= 100; - *buf++ = static_cast('0' + k / 10); + *buf++ = static_cast('0' + (k / 10)); k %= 10; *buf++ = static_cast('0' + k); } @@ -1095,7 +1095,7 @@ char* to_chars(char* first, const char* last, FloatType value) // Compute v = buffer * 10^decimal_exponent. // The decimal digits are stored in the buffer, which needs to be interpreted // as an unsigned decimal integer. - // len is the length of the buffer, i.e. the number of decimal digits. + // len is the length of the buffer, i.e., the number of decimal digits. int len = 0; int decimal_exponent = 0; dtoa_impl::grisu2(first, len, decimal_exponent, value); diff --git a/include/nlohmann/detail/conversions/to_json.hpp b/include/nlohmann/detail/conversions/to_json.hpp index 562089c330..3626593d31 100644 --- a/include/nlohmann/detail/conversions/to_json.hpp +++ b/include/nlohmann/detail/conversions/to_json.hpp @@ -1,16 +1,22 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once +#include // JSON_HAS_CPP_17 +#ifdef JSON_HAS_CPP_17 + #include // optional +#endif + #include // copy #include // begin, end -#include // string +#include // allocator_traits +#include // basic_string, char_traits #include // tuple, get #include // is_same, is_constructible, is_floating_point, is_enum, underlying_type #include // move, forward, declval, pair @@ -18,7 +24,6 @@ #include // vector #include -#include #include #include #include @@ -260,6 +265,22 @@ struct external_constructor // to_json // ///////////// +#ifdef JSON_HAS_CPP_17 +template::value, int> = 0> +void to_json(BasicJsonType& j, const std::optional& opt) noexcept +{ + if (opt.has_value()) + { + j = *opt; + } + else + { + j = nullptr; + } +} +#endif + template::value, int> = 0> inline void to_json(BasicJsonType& j, T b) noexcept @@ -406,6 +427,13 @@ inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence< j = { std::get(t)... }; } +template +inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& /*unused*/, index_sequence<> /*unused*/) +{ + using array_t = typename BasicJsonType::array_t; + j = array_t(); +} + template::value, int > = 0> inline void to_json(BasicJsonType& j, const T& t) { @@ -413,10 +441,21 @@ inline void to_json(BasicJsonType& j, const T& t) } #if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM +#if defined(__cpp_lib_char8_t) +template +inline void to_json(BasicJsonType& j, const std::basic_string& s) +{ + using OtherAllocator = typename std::allocator_traits::template rebind_alloc; + j = std::basic_string, OtherAllocator>(s.begin(), s.end(), s.get_allocator()); +} +#endif + template inline void to_json(BasicJsonType& j, const std_fs::path& p) { - j = p.string(); + // Returns either a std::string or a std::u8string depending whether library + // support for char8_t is enabled. + j = p.u8string(); } #endif diff --git a/include/nlohmann/detail/exceptions.hpp b/include/nlohmann/detail/exceptions.hpp index 5974d7be2b..cb87bb93ef 100644 --- a/include/nlohmann/detail/exceptions.hpp +++ b/include/nlohmann/detail/exceptions.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once @@ -25,6 +25,18 @@ #include #include +// With -Wweak-vtables, Clang will complain about the exception classes as they +// have no out-of-line virtual method definitions and their vtable will be +// emitted in every translation unit. This issue cannot be fixed with a +// header-only library as there is no implementation file to move these +// functions to. As a result, we suppress this warning here to avoid client +// code stumbling over this. See https://github.com/nlohmann/json/issues/4087 +// for a discussion. +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wweak-vtables" +#endif + NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { @@ -119,16 +131,34 @@ class exception : public std::exception { return concat(a, '/', detail::escape(b)); }); - return concat('(', str, ") "); + + return concat('(', str, ") ", get_byte_positions(leaf_element)); #else - static_cast(leaf_element); - return ""; + return get_byte_positions(leaf_element); #endif } private: /// an exception object as storage for error messages std::runtime_error m; +#if JSON_DIAGNOSTIC_POSITIONS + template + static std::string get_byte_positions(const BasicJsonType* leaf_element) + { + if ((leaf_element->start_pos() != std::string::npos) && (leaf_element->end_pos() != std::string::npos)) + { + return concat("(bytes ", std::to_string(leaf_element->start_pos()), "-", std::to_string(leaf_element->end_pos()), ") "); + } + return ""; + } +#else + template + static std::string get_byte_positions(const BasicJsonType* leaf_element) + { + static_cast(leaf_element); + return ""; + } +#endif }; /// @brief exception indicating a parse error @@ -255,3 +285,7 @@ class other_error : public exception } // namespace detail NLOHMANN_JSON_NAMESPACE_END + +#if defined(__clang__) + #pragma clang diagnostic pop +#endif diff --git a/include/nlohmann/detail/hash.hpp b/include/nlohmann/detail/hash.hpp index 4464e8e67b..61b3469f10 100644 --- a/include/nlohmann/detail/hash.hpp +++ b/include/nlohmann/detail/hash.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp index a6e100e761..c6453f74cf 100644 --- a/include/nlohmann/detail/input/binary_reader.hpp +++ b/include/nlohmann/detail/input/binary_reader.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once @@ -20,6 +20,9 @@ #include // char_traits, string #include // make_pair, move #include // vector +#ifdef __cpp_lib_byteswap + #include //byteswap +#endif #include #include @@ -50,7 +53,7 @@ enum class cbor_tag_handler_t @note from https://stackoverflow.com/a/1001328/266378 */ -static inline bool little_endianness(int num = 1) noexcept +inline bool little_endianness(int num = 1) noexcept { return *reinterpret_cast(&num) == 1; } @@ -62,7 +65,7 @@ static inline bool little_endianness(int num = 1) noexcept /*! @brief deserialization of CBOR, MessagePack, and UBJSON values */ -template> +template> class binary_reader { using number_integer_t = typename BasicJsonType::number_integer_t; @@ -169,7 +172,7 @@ class binary_reader std::int32_t document_size{}; get_number(input_format_t::bson, document_size); - if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast(-1)))) + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(detail::unknown_size()))) { return false; } @@ -325,7 +328,13 @@ class binary_reader return get_number(input_format_t::bson, value) && sax->number_integer(value); } - default: // anything else not supported (yet) + case 0x11: // uint64 + { + std::uint64_t value{}; + return get_number(input_format_t::bson, value) && sax->number_unsigned(value); + } + + default: // anything else is not supported (yet) { std::array cr{{}}; static_cast((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(element_type))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) @@ -391,7 +400,7 @@ class binary_reader std::int32_t document_size{}; get_number(input_format_t::bson, document_size); - if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast(-1)))) + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(detail::unknown_size()))) { return false; } @@ -651,7 +660,7 @@ class binary_reader } case 0x9F: // array (indefinite length) - return get_cbor_array(static_cast(-1), tag_handler); + return get_cbor_array(detail::unknown_size(), tag_handler); // map (0x00..0x17 pairs of data items follow) case 0xA0: @@ -705,7 +714,7 @@ class binary_reader } case 0xBF: // map (indefinite length) - return get_cbor_object(static_cast(-1), tag_handler); + return get_cbor_object(detail::unknown_size(), tag_handler); case 0xC6: // tagged item case 0xC7: @@ -722,7 +731,7 @@ class binary_reader case 0xD2: case 0xD3: case 0xD4: - case 0xD8: // tagged item (1 bytes follow) + case 0xD8: // tagged item (1 byte follows) case 0xD9: // tagged item (2 bytes follow) case 0xDA: // tagged item (4 bytes follow) case 0xDB: // tagged item (8 bytes follow) @@ -774,7 +783,7 @@ class binary_reader case cbor_tag_handler_t::store: { binary_t b; - // use binary subtype and store in binary container + // use binary subtype and store in a binary container switch (current) { case 0xD8: @@ -843,7 +852,7 @@ class binary_reader const auto byte1 = static_cast(byte1_raw); const auto byte2 = static_cast(byte2_raw); - // code from RFC 7049, Appendix D, Figure 3: + // Code from RFC 7049, Appendix D, Figure 3: // As half-precision floating-point numbers were only added // to IEEE 754 in 2008, today's programming platforms often // still only have limited support for them. It is very @@ -1093,7 +1102,7 @@ class binary_reader } /*! - @param[in] len the length of the array or static_cast(-1) for an + @param[in] len the length of the array or detail::unknown_size() for an array of indefinite size @param[in] tag_handler how CBOR tags should be treated @return whether array creation completed @@ -1106,7 +1115,7 @@ class binary_reader return false; } - if (len != static_cast(-1)) + if (len != detail::unknown_size()) { for (std::size_t i = 0; i < len; ++i) { @@ -1131,7 +1140,7 @@ class binary_reader } /*! - @param[in] len the length of the object or static_cast(-1) for an + @param[in] len the length of the object or detail::unknown_size() for an object of indefinite size @param[in] tag_handler how CBOR tags should be treated @return whether object creation completed @@ -1147,7 +1156,7 @@ class binary_reader if (len != 0) { string_t key; - if (len != static_cast(-1)) + if (len != detail::unknown_size()) { for (std::size_t i = 0; i < len; ++i) { @@ -2150,7 +2159,7 @@ class binary_reader { break; } - if (is_ndarray) // ndarray dimensional vector can only contain integers, and can not embed another array + if (is_ndarray) // ndarray dimensional vector can only contain integers and cannot embed another array { return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, exception_message(input_format, "ndarray dimensional vector is not allowed", "size"), nullptr)); } @@ -2183,8 +2192,16 @@ class binary_reader result = 1; for (auto i : dim) { + // Pre-multiplication overflow check: if i > 0 and result > SIZE_MAX/i, then result*i would overflow. + // This check must happen before multiplication since overflow detection after the fact is unreliable + // as modular arithmetic can produce any value, not just 0 or SIZE_MAX. + if (JSON_HEDLEY_UNLIKELY(i > 0 && result > (std::numeric_limits::max)() / i)) + { + return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr)); + } result *= i; - if (result == 0 || result == npos) // because dim elements shall not have zeros, result = 0 means overflow happened; it also can't be npos as it is used to initialize size in get_ubjson_size_type() + // Additional post-multiplication check to catch any edge cases the pre-check might miss + if (result == 0 || result == npos) { return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr)); } @@ -2310,6 +2327,16 @@ class binary_reader case 'Z': // null return sax->null(); + case 'B': // byte + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint8_t number{}; + return get_number(input_format, number) && sax->number_unsigned(number); + } + case 'U': { std::uint8_t number{}; @@ -2390,7 +2417,7 @@ class binary_reader const auto byte1 = static_cast(byte1_raw); const auto byte2 = static_cast(byte2_raw); - // code from RFC 7049, Appendix D, Figure 3: + // Code from RFC 7049, Appendix D, Figure 3: // As half-precision floating-point numbers were only added // to IEEE 754 in 2008, today's programming platforms often // still only have limited support for them. It is very @@ -2510,7 +2537,7 @@ class binary_reader return false; } - if (size_and_type.second == 'C') + if (size_and_type.second == 'C' || size_and_type.second == 'B') { size_and_type.second = 'U'; } @@ -2532,6 +2559,13 @@ class binary_reader return (sax->end_array() && sax->end_object()); } + // If BJData type marker is 'B' decode as binary + if (input_format == input_format_t::bjdata && size_and_type.first != npos && size_and_type.second == 'B') + { + binary_t result; + return get_binary(input_format, size_and_type.first, result) && sax->binary(result); + } + if (size_and_type.first != npos) { if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first))) @@ -2565,7 +2599,7 @@ class binary_reader } else { - if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast(-1)))) + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(detail::unknown_size()))) { return false; } @@ -2643,7 +2677,7 @@ class binary_reader } else { - if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast(-1)))) + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(detail::unknown_size()))) { return false; } @@ -2671,7 +2705,7 @@ class binary_reader bool get_ubjson_high_precision_number() { - // get size of following number string + // get the size of the following number string std::size_t size{}; bool no_ndarray = true; auto res = get_ubjson_size_value(size, no_ndarray); @@ -2754,6 +2788,29 @@ class binary_reader return current = ia.get_character(); } + /*! + @brief get_to read into a primitive type + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns false instead + + @return bool, whether the read was successful + */ + template + bool get_to(T& dest, const input_format_t format, const char* context) + { + auto new_chars_read = ia.get_elements(&dest); + chars_read += new_chars_read; + if (JSON_HEDLEY_UNLIKELY(new_chars_read < sizeof(T))) + { + // in case of failure, advance position by 1 to report the failing location + ++chars_read; + sax->parse_error(chars_read, "", parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), nullptr)); + return false; + } + return true; + } + /*! @return character read from the input after ignoring all 'N' entries */ @@ -2768,6 +2825,33 @@ class binary_reader return current; } + template + static void byte_swap(NumberType& number) + { + constexpr std::size_t sz = sizeof(number); +#ifdef __cpp_lib_byteswap + if constexpr (sz == 1) + { + return; + } + else if constexpr(std::is_integral_v) + { + number = std::byteswap(number); + return; + } + else + { +#endif + auto* ptr = reinterpret_cast(&number); + for (std::size_t i = 0; i < sz / 2; ++i) + { + std::swap(ptr[i], ptr[sz - i - 1]); + } +#ifdef __cpp_lib_byteswap + } +#endif + } + /* @brief read a number from the input @@ -2786,29 +2870,16 @@ class binary_reader template bool get_number(const input_format_t format, NumberType& result) { - // step 1: read input into array with system's byte order - std::array vec{}; - for (std::size_t i = 0; i < sizeof(NumberType); ++i) - { - get(); - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number"))) - { - return false; - } + // read in the original format - // reverse byte order prior to conversion if necessary - if (is_little_endian != (InputIsLittleEndian || format == input_format_t::bjdata)) - { - vec[sizeof(NumberType) - i - 1] = static_cast(current); - } - else - { - vec[i] = static_cast(current); // LCOV_EXCL_LINE - } + if (JSON_HEDLEY_UNLIKELY(!get_to(result, format, "number"))) + { + return false; + } + if (is_little_endian != (InputIsLittleEndian || format == input_format_t::bjdata)) + { + byte_swap(result); } - - // step 2: convert array into number of type T and return - std::memcpy(&result, vec.data(), sizeof(NumberType)); return true; } @@ -2873,7 +2944,7 @@ class binary_reader success = false; break; } - result.push_back(static_cast(current)); + result.push_back(static_cast(current)); } return success; } @@ -2947,7 +3018,7 @@ class binary_reader } private: - static JSON_INLINE_VARIABLE constexpr std::size_t npos = static_cast(-1); + static JSON_INLINE_VARIABLE constexpr std::size_t npos = detail::unknown_size(); /// input adapter InputAdapterType ia; @@ -2973,6 +3044,7 @@ class binary_reader #define JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_ \ make_array( \ + bjd_type{'B', "byte"}, \ bjd_type{'C', "char"}, \ bjd_type{'D', "double"}, \ bjd_type{'I', "int16"}, \ diff --git a/include/nlohmann/detail/input/input_adapters.hpp b/include/nlohmann/detail/input/input_adapters.hpp index 33fca3e4b9..be58b1110f 100644 --- a/include/nlohmann/detail/input/input_adapters.hpp +++ b/include/nlohmann/detail/input/input_adapters.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once @@ -14,6 +14,7 @@ #include // begin, end, iterator_traits, random_access_iterator_tag, distance, next #include // shared_ptr, make_shared, addressof #include // accumulate +#include // streambuf #include // string, char_traits #include // enable_if, is_base_of, is_pointer, is_integral, remove_pointer #include // pair, declval @@ -23,6 +24,7 @@ #include // istream #endif // JSON_NO_IO +#include #include #include #include @@ -67,6 +69,13 @@ class file_input_adapter return std::fgetc(m_file); } + // returns the number of characters successfully read + template + std::size_t get_elements(T* dest, std::size_t count = 1) + { + return fread(dest, 1, sizeof(T) * count, m_file); + } + private: /// the file pointer to read from std::FILE* m_file; @@ -100,7 +109,7 @@ class input_stream_adapter : is(&i), sb(i.rdbuf()) {} - // delete because of pointer members + // deleted because of pointer members input_stream_adapter(const input_stream_adapter&) = delete; input_stream_adapter& operator=(input_stream_adapter&) = delete; input_stream_adapter& operator=(input_stream_adapter&&) = delete; @@ -114,7 +123,7 @@ class input_stream_adapter // std::istream/std::streambuf use std::char_traits::to_int_type, to // ensure that std::char_traits::eof() and the character 0xFF do not - // end up as the same value, e.g. 0xFFFFFFFF. + // end up as the same value, e.g., 0xFFFFFFFF. std::char_traits::int_type get_character() { auto res = sb->sbumpc(); @@ -126,6 +135,17 @@ class input_stream_adapter return res; } + template + std::size_t get_elements(T* dest, std::size_t count = 1) + { + auto res = static_cast(sb->sgetn(reinterpret_cast(dest), static_cast(count * sizeof(T)))); + if (JSON_HEDLEY_UNLIKELY(res < count * sizeof(T))) + { + is->clear(is->rdstate() | std::ios::eofbit); + } + return res; + } + private: /// the associated input stream std::istream* is = nullptr; @@ -157,6 +177,26 @@ class iterator_input_adapter return char_traits::eof(); } + // for general iterators, we cannot really do something better than falling back to processing the range one-by-one + template + std::size_t get_elements(T* dest, std::size_t count = 1) + { + auto* ptr = reinterpret_cast(dest); + for (std::size_t read_index = 0; read_index < count * sizeof(T); ++read_index) + { + if (JSON_HEDLEY_LIKELY(current != end)) + { + ptr[read_index] = static_cast(*current); + std::advance(current, 1); + } + else + { + return read_index; + } + } + return count * sizeof(T); + } + private: IteratorType current; IteratorType end; @@ -305,7 +345,7 @@ class wide_string_input_adapter typename std::char_traits::int_type get_character() noexcept { - // check if buffer needs to be filled + // check if the buffer needs to be filled if (utf8_bytes_index == utf8_bytes_filled) { fill_buffer(); @@ -320,6 +360,13 @@ class wide_string_input_adapter return utf8_bytes[utf8_bytes_index++]; } + // parsing binary with wchar doesn't make sense, but since the parsing mode can be runtime, we need something here + template + std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1) + { + JSON_THROW(parse_error::create(112, 1, "wide string type cannot be interpreted as binary data", nullptr)); + } + private: BaseInputAdapter base_adapter; @@ -355,7 +402,7 @@ template struct is_iterator_of_multibyte { using value_type = typename std::iterator_traits::value_type; - enum + enum // NOLINT(cppcoreguidelines-use-enum-class) { value = sizeof(value_type) > 1 }; @@ -416,10 +463,17 @@ typename container_input_adapter_factory_impl::container_input_adapter_factory::create(container); } +// specialization for std::string +using string_input_adapter_type = decltype(input_adapter(std::declval())); + #ifndef JSON_NO_IO // Special cases with fast paths inline file_input_adapter input_adapter(std::FILE* file) { + if (file == nullptr) + { + JSON_THROW(parse_error::create(101, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr)); + } return file_input_adapter(file); } @@ -446,9 +500,13 @@ template < typename CharT, int >::type = 0 > contiguous_bytes_input_adapter input_adapter(CharT b) { + if (b == nullptr) + { + JSON_THROW(parse_error::create(101, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr)); + } auto length = std::strlen(reinterpret_cast(b)); const auto* ptr = reinterpret_cast(b); - return input_adapter(ptr, ptr + length); + return input_adapter(ptr, ptr + length); // cppcheck-suppress[nullPointerArithmeticRedundantCheck] } template diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index 90583d671b..ea41322326 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -1,22 +1,23 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once #include #include // string +#include // enable_if_t #include // move #include // vector #include +#include #include #include - NLOHMANN_JSON_NAMESPACE_BEGIN /*! @@ -144,6 +145,11 @@ struct json_sax namespace detail { +constexpr std::size_t unknown_size() +{ + return (std::numeric_limits::max)(); +} + /*! @brief SAX implementation to create a JSON value from SAX events @@ -157,7 +163,7 @@ constructor contains the parsed value. @tparam BasicJsonType the JSON type */ -template +template class json_sax_dom_parser { public: @@ -166,14 +172,15 @@ class json_sax_dom_parser using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; using binary_t = typename BasicJsonType::binary_t; + using lexer_t = lexer; /*! @param[in,out] r reference to a JSON value that is manipulated while parsing @param[in] allow_exceptions_ whether parse errors yield exceptions */ - explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true) - : root(r), allow_exceptions(allow_exceptions_) + explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true, lexer_t* lexer_ = nullptr) + : root(r), allow_exceptions(allow_exceptions_), m_lexer_ref(lexer_) {} // make class move-only @@ -229,7 +236,18 @@ class json_sax_dom_parser { ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); - if (JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) +#if JSON_DIAGNOSTIC_POSITIONS + // Manually set the start position of the object here. + // Ensure this is after the call to handle_value to ensure correct start position. + if (m_lexer_ref) + { + // Lexer has read the first character of the object, so + // subtract 1 from the position to get the correct start position. + ref_stack.back()->start_position = m_lexer_ref->get_position() - 1; + } +#endif + + if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size())) { JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back())); } @@ -242,7 +260,7 @@ class json_sax_dom_parser JSON_ASSERT(!ref_stack.empty()); JSON_ASSERT(ref_stack.back()->is_object()); - // add null at given key and store the reference for later + // add null at the given key and store the reference for later object_element = &(ref_stack.back()->m_data.m_value.object->operator[](val)); return true; } @@ -252,6 +270,14 @@ class json_sax_dom_parser JSON_ASSERT(!ref_stack.empty()); JSON_ASSERT(ref_stack.back()->is_object()); +#if JSON_DIAGNOSTIC_POSITIONS + if (m_lexer_ref) + { + // Lexer's position is past the closing brace, so set that as the end position. + ref_stack.back()->end_position = m_lexer_ref->get_position(); + } +#endif + ref_stack.back()->set_parents(); ref_stack.pop_back(); return true; @@ -261,7 +287,16 @@ class json_sax_dom_parser { ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); - if (JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) +#if JSON_DIAGNOSTIC_POSITIONS + // Manually set the start position of the array here. + // Ensure this is after the call to handle_value to ensure correct start position. + if (m_lexer_ref) + { + ref_stack.back()->start_position = m_lexer_ref->get_position() - 1; + } +#endif + + if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size())) { JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back())); } @@ -274,6 +309,14 @@ class json_sax_dom_parser JSON_ASSERT(!ref_stack.empty()); JSON_ASSERT(ref_stack.back()->is_array()); +#if JSON_DIAGNOSTIC_POSITIONS + if (m_lexer_ref) + { + // Lexer's position is past the closing bracket, so set that as the end position. + ref_stack.back()->end_position = m_lexer_ref->get_position(); + } +#endif + ref_stack.back()->set_parents(); ref_stack.pop_back(); return true; @@ -298,6 +341,75 @@ class json_sax_dom_parser } private: + +#if JSON_DIAGNOSTIC_POSITIONS + void handle_diagnostic_positions_for_json_value(BasicJsonType& v) + { + if (m_lexer_ref) + { + // Lexer has read past the current field value, so set the end position to the current position. + // The start position will be set below based on the length of the string representation + // of the value. + v.end_position = m_lexer_ref->get_position(); + + switch (v.type()) + { + case value_t::boolean: + { + // 4 and 5 are the string length of "true" and "false" + v.start_position = v.end_position - (v.m_data.m_value.boolean ? 4 : 5); + break; + } + + case value_t::null: + { + // 4 is the string length of "null" + v.start_position = v.end_position - 4; + break; + } + + case value_t::string: + { + // include the length of the quotes, which is 2 + v.start_position = v.end_position - v.m_data.m_value.string->size() - 2; + break; + } + + // As we handle the start and end positions for values created during parsing, + // we do not expect the following value type to be called. Regardless, set the positions + // in case this is created manually or through a different constructor. Exclude from lcov + // since the exact condition of this switch is esoteric. + // LCOV_EXCL_START + case value_t::discarded: + { + v.end_position = std::string::npos; + v.start_position = v.end_position; + break; + } + // LCOV_EXCL_STOP + case value_t::binary: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + { + v.start_position = v.end_position - m_lexer_ref->get_string().size(); + break; + } + case value_t::object: + case value_t::array: + { + // object and array are handled in start_object() and start_array() handlers + // skip setting the values here. + break; + } + default: // LCOV_EXCL_LINE + // Handle all possible types discretely, default handler should never be reached. + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert,-warnings-as-errors) LCOV_EXCL_LINE + } + } + } +#endif + /*! @invariant If the ref stack is empty, then the passed value will be the new root. @@ -311,6 +423,11 @@ class json_sax_dom_parser if (ref_stack.empty()) { root = BasicJsonType(std::forward(v)); + +#if JSON_DIAGNOSTIC_POSITIONS + handle_diagnostic_positions_for_json_value(root); +#endif + return &root; } @@ -319,12 +436,22 @@ class json_sax_dom_parser if (ref_stack.back()->is_array()) { ref_stack.back()->m_data.m_value.array->emplace_back(std::forward(v)); + +#if JSON_DIAGNOSTIC_POSITIONS + handle_diagnostic_positions_for_json_value(ref_stack.back()->m_data.m_value.array->back()); +#endif + return &(ref_stack.back()->m_data.m_value.array->back()); } JSON_ASSERT(ref_stack.back()->is_object()); JSON_ASSERT(object_element); *object_element = BasicJsonType(std::forward(v)); + +#if JSON_DIAGNOSTIC_POSITIONS + handle_diagnostic_positions_for_json_value(*object_element); +#endif + return object_element; } @@ -338,9 +465,11 @@ class json_sax_dom_parser bool errored = false; /// whether to throw exceptions in case of errors const bool allow_exceptions = true; + /// the lexer reference to obtain the current position + lexer_t* m_lexer_ref = nullptr; }; -template +template class json_sax_dom_callback_parser { public: @@ -351,11 +480,13 @@ class json_sax_dom_callback_parser using binary_t = typename BasicJsonType::binary_t; using parser_callback_t = typename BasicJsonType::parser_callback_t; using parse_event_t = typename BasicJsonType::parse_event_t; + using lexer_t = lexer; json_sax_dom_callback_parser(BasicJsonType& r, - const parser_callback_t cb, - const bool allow_exceptions_ = true) - : root(r), callback(cb), allow_exceptions(allow_exceptions_) + parser_callback_t cb, + const bool allow_exceptions_ = true, + lexer_t* lexer_ = nullptr) + : root(r), callback(std::move(cb)), allow_exceptions(allow_exceptions_), m_lexer_ref(lexer_) { keep_stack.push_back(true); } @@ -418,12 +549,26 @@ class json_sax_dom_callback_parser auto val = handle_value(BasicJsonType::value_t::object, true); ref_stack.push_back(val.second); - // check object limit - if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) + if (ref_stack.back()) { - JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back())); - } +#if JSON_DIAGNOSTIC_POSITIONS + // Manually set the start position of the object here. + // Ensure this is after the call to handle_value to ensure correct start position. + if (m_lexer_ref) + { + // Lexer has read the first character of the object, so + // subtract 1 from the position to get the correct start position. + ref_stack.back()->start_position = m_lexer_ref->get_position() - 1; + } +#endif + + // check object limit + if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back())); + } + } return true; } @@ -431,11 +576,11 @@ class json_sax_dom_callback_parser { BasicJsonType k = BasicJsonType(val); - // check callback for key + // check callback for the key const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::key, k); key_keep_stack.push_back(keep); - // add discarded value at given key and store the reference for later + // add discarded value at the given key and store the reference for later if (keep && ref_stack.back()) { object_element = &(ref_stack.back()->m_data.m_value.object->operator[](val) = discarded); @@ -452,9 +597,23 @@ class json_sax_dom_callback_parser { // discard object *ref_stack.back() = discarded; + +#if JSON_DIAGNOSTIC_POSITIONS + // Set start/end positions for discarded object. + handle_diagnostic_positions_for_json_value(*ref_stack.back()); +#endif } else { + +#if JSON_DIAGNOSTIC_POSITIONS + if (m_lexer_ref) + { + // Lexer's position is past the closing brace, so set that as the end position. + ref_stack.back()->end_position = m_lexer_ref->get_position(); + } +#endif + ref_stack.back()->set_parents(); } } @@ -488,10 +647,25 @@ class json_sax_dom_callback_parser auto val = handle_value(BasicJsonType::value_t::array, true); ref_stack.push_back(val.second); - // check array limit - if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) + if (ref_stack.back()) { - JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back())); + +#if JSON_DIAGNOSTIC_POSITIONS + // Manually set the start position of the array here. + // Ensure this is after the call to handle_value to ensure correct start position. + if (m_lexer_ref) + { + // Lexer has read the first character of the array, so + // subtract 1 from the position to get the correct start position. + ref_stack.back()->start_position = m_lexer_ref->get_position() - 1; + } +#endif + + // check array limit + if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back())); + } } return true; @@ -506,12 +680,26 @@ class json_sax_dom_callback_parser keep = callback(static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); if (keep) { + +#if JSON_DIAGNOSTIC_POSITIONS + if (m_lexer_ref) + { + // Lexer's position is past the closing bracket, so set that as the end position. + ref_stack.back()->end_position = m_lexer_ref->get_position(); + } +#endif + ref_stack.back()->set_parents(); } else { // discard array *ref_stack.back() = discarded; + +#if JSON_DIAGNOSTIC_POSITIONS + // Set start/end positions for discarded array. + handle_diagnostic_positions_for_json_value(*ref_stack.back()); +#endif } } @@ -548,6 +736,71 @@ class json_sax_dom_callback_parser } private: + +#if JSON_DIAGNOSTIC_POSITIONS + void handle_diagnostic_positions_for_json_value(BasicJsonType& v) + { + if (m_lexer_ref) + { + // Lexer has read past the current field value, so set the end position to the current position. + // The start position will be set below based on the length of the string representation + // of the value. + v.end_position = m_lexer_ref->get_position(); + + switch (v.type()) + { + case value_t::boolean: + { + // 4 and 5 are the string length of "true" and "false" + v.start_position = v.end_position - (v.m_data.m_value.boolean ? 4 : 5); + break; + } + + case value_t::null: + { + // 4 is the string length of "null" + v.start_position = v.end_position - 4; + break; + } + + case value_t::string: + { + // include the length of the quotes, which is 2 + v.start_position = v.end_position - v.m_data.m_value.string->size() - 2; + break; + } + + case value_t::discarded: + { + v.end_position = std::string::npos; + v.start_position = v.end_position; + break; + } + + case value_t::binary: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + { + v.start_position = v.end_position - m_lexer_ref->get_string().size(); + break; + } + + case value_t::object: + case value_t::array: + { + // object and array are handled in start_object() and start_array() handlers + // skip setting the values here. + break; + } + default: // LCOV_EXCL_LINE + // Handle all possible types discretely, default handler should never be reached. + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert,-warnings-as-errors) LCOV_EXCL_LINE + } + } + } +#endif + /*! @param[in] v value to add to the JSON value we build during parsing @param[in] skip_callback whether we should skip calling the callback @@ -578,6 +831,10 @@ class json_sax_dom_callback_parser // create value auto value = BasicJsonType(std::forward(v)); +#if JSON_DIAGNOSTIC_POSITIONS + handle_diagnostic_positions_for_json_value(value); +#endif + // check callback const bool keep = skip_callback || callback(static_cast(ref_stack.size()), parse_event_t::value, value); @@ -645,6 +902,8 @@ class json_sax_dom_callback_parser const bool allow_exceptions = true; /// a discarded value for the callback BasicJsonType discarded = BasicJsonType::value_t::discarded; + /// the lexer reference to obtain the current position + lexer_t* m_lexer_ref = nullptr; }; template @@ -692,7 +951,7 @@ class json_sax_acceptor return true; } - bool start_object(std::size_t /*unused*/ = static_cast(-1)) + bool start_object(std::size_t /*unused*/ = detail::unknown_size()) { return true; } @@ -707,7 +966,7 @@ class json_sax_acceptor return true; } - bool start_array(std::size_t /*unused*/ = static_cast(-1)) + bool start_array(std::size_t /*unused*/ = detail::unknown_size()) { return true; } diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index 4b3bf77d62..3d63229f9b 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once @@ -127,7 +127,7 @@ class lexer : public lexer_base , decimal_point_char(static_cast(get_decimal_point())) {} - // delete because of pointer members + // deleted because of pointer members lexer(const lexer&) = delete; lexer(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) lexer& operator=(lexer&) = delete; @@ -262,10 +262,10 @@ class lexer : public lexer_base while (true) { - // get next character + // get the next character switch (get()) { - // end of file while parsing string + // end of file while parsing the string case char_traits::eof(): { error_message = "invalid string: missing closing quote"; @@ -351,7 +351,7 @@ class lexer : public lexer_base (static_cast(codepoint1) << 10u) // low surrogate occupies the least significant 15 bits + static_cast(codepoint2) - // there is still the 0xD800, 0xDC00 and 0x10000 noise + // there is still the 0xD800, 0xDC00, and 0x10000 noise // in the result, so we have to subtract with: // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 - 0x35FDC00u); @@ -377,7 +377,7 @@ class lexer : public lexer_base } } - // result of the above calculation yields a proper codepoint + // the result of the above calculation yields a proper codepoint JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF); // translate codepoint into bytes @@ -828,7 +828,7 @@ class lexer : public lexer_base break; } - // remaining bytes (80..C1 and F5..FF) are ill-formed + // the remaining bytes (80..C1 and F5..FF) are ill-formed default: { error_message = "invalid string: ill-formed UTF-8 byte"; @@ -967,13 +967,13 @@ class lexer : public lexer_base locale's decimal point is used instead of `.` to work with the locale-dependent converters. */ - token_type scan_number() // lgtm [cpp/use-of-goto] + token_type scan_number() // lgtm [cpp/use-of-goto] `goto` is used in this function to implement the number-parsing state machine described above. By design, any finite input will eventually reach the "done" state or return token_type::parse_error. In each intermediate state, 1 byte of the input is appended to the token_buffer vector, and only the already initialized variables token_buffer, number_type, and error_message are manipulated. { // reset token_buffer to store the number's bytes reset(); // the type of the parsed number; initially set to unsigned; will be - // changed if minus sign, decimal point or exponent is read + // changed if minus sign, decimal point, or exponent is read token_type number_type = token_type::value_unsigned; // state (init): we just found out we need to scan a number @@ -1049,6 +1049,7 @@ class lexer : public lexer_base case '.': { add(decimal_point_char); + decimal_point_position = token_buffer.size() - 1; goto scan_number_decimal1; } @@ -1085,6 +1086,7 @@ class lexer : public lexer_base case '.': { add(decimal_point_char); + decimal_point_position = token_buffer.size() - 1; goto scan_number_decimal1; } @@ -1245,7 +1247,7 @@ class lexer : public lexer_base // we are done scanning a number) unget(); - char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + char* endptr = nullptr; // NOLINT(misc-const-correctness,cppcoreguidelines-pro-type-vararg,hicpp-vararg) errno = 0; // try to parse integers first and fall back to floats @@ -1256,7 +1258,7 @@ class lexer : public lexer_base // we checked the number format before JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); - if (errno == 0) + if (errno != ERANGE) { value_unsigned = static_cast(x); if (value_unsigned == x) @@ -1272,7 +1274,7 @@ class lexer : public lexer_base // we checked the number format before JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); - if (errno == 0) + if (errno != ERANGE) { value_integer = static_cast(x); if (value_integer == x) @@ -1322,6 +1324,7 @@ class lexer : public lexer_base { token_buffer.clear(); token_string.clear(); + decimal_point_position = std::string::npos; token_string.push_back(char_traits::to_char_type(current)); } @@ -1342,7 +1345,7 @@ class lexer : public lexer_base if (next_unget) { - // just reset the next_unget variable and work with current + // only reset the next_unget variable and work with current next_unget = false; } else @@ -1430,6 +1433,11 @@ class lexer : public lexer_base /// return current string value (implicitly resets the token; useful only once) string_t& get_string() { + // translate decimal points from locale back to '.' (#4084) + if (decimal_point_char != '.' && decimal_point_position != std::string::npos) + { + token_buffer[decimal_point_position] = '.'; + } return token_buffer; } @@ -1516,7 +1524,7 @@ class lexer : public lexer_base return token_type::parse_error; } - // read next character and ignore whitespace + // read the next character and ignore whitespace skip_whitespace(); // ignore comments @@ -1627,6 +1635,8 @@ class lexer : public lexer_base /// the decimal point const char_int_type decimal_point_char = '.'; + /// the position of the decimal point in the input + std::size_t decimal_point_position = std::string::npos; }; } // namespace detail diff --git a/include/nlohmann/detail/input/parser.hpp b/include/nlohmann/detail/input/parser.hpp index bdf85ba292..4b1963f4a7 100644 --- a/include/nlohmann/detail/input/parser.hpp +++ b/include/nlohmann/detail/input/parser.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once @@ -69,12 +69,14 @@ class parser public: /// a parser reading from an input adapter explicit parser(InputAdapterType&& adapter, - const parser_callback_t cb = nullptr, + parser_callback_t cb = nullptr, const bool allow_exceptions_ = true, - const bool skip_comments = false) - : callback(cb) - , m_lexer(std::move(adapter), skip_comments) + const bool ignore_comments = false, + const bool ignore_trailing_commas_ = false) + : callback(std::move(cb)) + , m_lexer(std::move(adapter), ignore_comments) , allow_exceptions(allow_exceptions_) + , ignore_trailing_commas(ignore_trailing_commas_) { // read first token get_token(); @@ -94,7 +96,7 @@ class parser { if (callback) { - json_sax_dom_callback_parser sdp(result, callback, allow_exceptions); + json_sax_dom_callback_parser sdp(result, callback, allow_exceptions, &m_lexer); sax_parse_internal(&sdp); // in strict mode, input must be completely read @@ -106,7 +108,7 @@ class parser exception_message(token_type::end_of_input, "value"), nullptr)); } - // in case of an error, return discarded value + // in case of an error, return a discarded value if (sdp.is_errored()) { result = value_t::discarded; @@ -122,7 +124,7 @@ class parser } else { - json_sax_dom_parser sdp(result, allow_exceptions); + json_sax_dom_parser sdp(result, allow_exceptions, &m_lexer); sax_parse_internal(&sdp); // in strict mode, input must be completely read @@ -133,7 +135,7 @@ class parser parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr)); } - // in case of an error, return discarded value + // in case of an error, return a discarded value if (sdp.is_errored()) { result = value_t::discarded; @@ -194,7 +196,7 @@ class parser { case token_type::begin_object: { - if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast(-1)))) + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(detail::unknown_size()))) { return false; } @@ -239,7 +241,7 @@ class parser case token_type::begin_array: { - if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast(-1)))) + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(detail::unknown_size()))) { return false; } @@ -336,7 +338,7 @@ class parser case token_type::parse_error: { - // using "uninitialized" to avoid "expected" message + // using "uninitialized" to avoid an "expected" message return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr)); @@ -384,11 +386,17 @@ 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(); - continue; + + // 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 ] @@ -417,32 +425,39 @@ 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) { - // parse key - if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string)) - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr)); - } + get_token(); - if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) + // if ignore_trailing_commas and last_token is }, we can continue to "closing }" + if (!(ignore_trailing_commas && last_token == token_type::end_object)) { - return false; - } + // parse key + if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr)); + } - // parse separator (:) - if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr)); - } + if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) + { + return false; + } - // parse values - get_token(); - continue; + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr)); + } + + // parse values + get_token(); + continue; + } } // closing } @@ -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 diff --git a/include/nlohmann/detail/input/position_t.hpp b/include/nlohmann/detail/input/position_t.hpp index 8ac7c78cfd..25b0a6a840 100644 --- a/include/nlohmann/detail/input/position_t.hpp +++ b/include/nlohmann/detail/input/position_t.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once diff --git a/include/nlohmann/detail/iterators/internal_iterator.hpp b/include/nlohmann/detail/iterators/internal_iterator.hpp index 2991ee6930..c216ed271e 100644 --- a/include/nlohmann/detail/iterators/internal_iterator.hpp +++ b/include/nlohmann/detail/iterators/internal_iterator.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once diff --git a/include/nlohmann/detail/iterators/iter_impl.hpp b/include/nlohmann/detail/iterators/iter_impl.hpp index 4447091347..93e3216b95 100644 --- a/include/nlohmann/detail/iterators/iter_impl.hpp +++ b/include/nlohmann/detail/iterators/iter_impl.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once @@ -23,7 +23,7 @@ NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { -// forward declare, to be able to friend it later on +// forward declare to be able to friend it later on template class iteration_proxy; template class iteration_proxy_value; @@ -463,7 +463,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci /*! @brief comparison: equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. + @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized. */ template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > bool operator==(const IterImpl& other) const @@ -474,7 +474,11 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object)); } - JSON_ASSERT(m_object != nullptr); + // value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493 + if (m_object == nullptr) + { + return true; + } switch (m_object->m_data.m_type) { @@ -499,7 +503,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci /*! @brief comparison: not equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. + @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized. */ template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > bool operator!=(const IterImpl& other) const @@ -509,7 +513,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci /*! @brief comparison: smaller - @pre The iterator is initialized; i.e. `m_object != nullptr`. + @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized. */ bool operator<(const iter_impl& other) const { @@ -519,7 +523,12 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object)); } - JSON_ASSERT(m_object != nullptr); + // value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493 + if (m_object == nullptr) + { + // the iterators are both value-initialized and are to be considered equal, but this function checks for smaller, so we return false + return false; + } switch (m_object->m_data.m_type) { @@ -544,7 +553,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci /*! @brief comparison: less than or equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. + @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized. */ bool operator<=(const iter_impl& other) const { @@ -553,7 +562,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci /*! @brief comparison: greater than - @pre The iterator is initialized; i.e. `m_object != nullptr`. + @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized. */ bool operator>(const iter_impl& other) const { @@ -562,7 +571,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci /*! @brief comparison: greater than or equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. + @pre (1) The iterator is initialized; i.e. `m_object != nullptr`, or (2) both iterators are value-initialized. */ bool operator>=(const iter_impl& other) const { diff --git a/include/nlohmann/detail/iterators/iteration_proxy.hpp b/include/nlohmann/detail/iterators/iteration_proxy.hpp index 76293de227..99246d1203 100644 --- a/include/nlohmann/detail/iterators/iteration_proxy.hpp +++ b/include/nlohmann/detail/iterators/iteration_proxy.hpp @@ -1,16 +1,15 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once #include // size_t -#include // input_iterator_tag -#include // string, to_string +#include // forward_iterator_tag #include // tuple_size, get, tuple_element #include // move @@ -20,19 +19,13 @@ #include #include +#include #include NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { -template -void int_to_string( string_type& target, std::size_t value ) -{ - // For ADL - using std::to_string; - target = to_string(value); -} template class iteration_proxy_value { public: @@ -40,7 +33,7 @@ template class iteration_proxy_value using value_type = iteration_proxy_value; using pointer = value_type *; using reference = value_type &; - using iterator_category = std::input_iterator_tag; + using iterator_category = std::forward_iterator_tag; using string_type = typename std::remove_cv< typename std::remove_reference().key() ) >::type >::type; private: @@ -220,7 +213,7 @@ namespace std #endif template class tuple_size<::nlohmann::detail::iteration_proxy_value> // NOLINT(cert-dcl58-cpp) - : public std::integral_constant {}; + : public std::integral_constant {}; template class tuple_element> // NOLINT(cert-dcl58-cpp) diff --git a/include/nlohmann/detail/iterators/iterator_traits.hpp b/include/nlohmann/detail/iterators/iterator_traits.hpp index 84cc27a85e..54307ef817 100644 --- a/include/nlohmann/detail/iterators/iterator_traits.hpp +++ b/include/nlohmann/detail/iterators/iterator_traits.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once @@ -43,7 +43,7 @@ struct iterator_traits template struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> - : iterator_types + : iterator_types { }; diff --git a/include/nlohmann/detail/iterators/json_reverse_iterator.hpp b/include/nlohmann/detail/iterators/json_reverse_iterator.hpp index 006d5499ad..59cec75b4e 100644 --- a/include/nlohmann/detail/iterators/json_reverse_iterator.hpp +++ b/include/nlohmann/detail/iterators/json_reverse_iterator.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once diff --git a/include/nlohmann/detail/iterators/primitive_iterator.hpp b/include/nlohmann/detail/iterators/primitive_iterator.hpp index 0b6e8499e6..e6a536919d 100644 --- a/include/nlohmann/detail/iterators/primitive_iterator.hpp +++ b/include/nlohmann/detail/iterators/primitive_iterator.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once @@ -21,9 +21,9 @@ namespace detail @brief an iterator for primitive JSON types This class models an iterator for primitive JSON types (boolean, number, -string). It's only purpose is to allow the iterator/const_iterator classes +string). Its only purpose is to allow the iterator/const_iterator classes to "iterate" over primitive values. Internally, the iterator is modeled by -a `difference_type` variable. Value begin_value (`0`) models the begin, +a `difference_type` variable. Value begin_value (`0`) models the begin and end_value (`1`) models past the end. */ class primitive_iterator_t diff --git a/include/nlohmann/detail/json_custom_base_class.hpp b/include/nlohmann/detail/json_custom_base_class.hpp index d1e29162a3..a4b86f2b49 100644 --- a/include/nlohmann/detail/json_custom_base_class.hpp +++ b/include/nlohmann/detail/json_custom_base_class.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index 4fdcd9ad28..ac50a8703b 100644 --- a/include/nlohmann/detail/json_pointer.hpp +++ b/include/nlohmann/detail/json_pointer.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once @@ -228,7 +228,7 @@ class json_pointer } const char* p = s.c_str(); - char* p_end = nullptr; + char* p_end = nullptr; // NOLINT(misc-const-correctness) errno = 0; // strtoull doesn't reset errno const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int) if (p == p_end // invalid input or empty string @@ -285,7 +285,7 @@ class json_pointer { if (reference_token == "0") { - // start a new array if reference token is 0 + // start a new array if the reference token is 0 result = &result->operator[](0); } else @@ -314,7 +314,7 @@ class json_pointer The following code is only reached if there exists a reference token _and_ the current value is primitive. In this case, we have an error situation, because primitive values may only occur as - single value; that is, with an empty list of reference tokens. + a single value; that is, with an empty list of reference tokens. */ case detail::value_t::string: case detail::value_t::boolean: @@ -358,7 +358,7 @@ class json_pointer // convert null values to arrays or objects before continuing if (ptr->is_null()) { - // check if reference token is a number + // check if the reference token is a number const bool nums = std::all_of(reference_token.begin(), reference_token.end(), [](const unsigned char x) @@ -366,7 +366,7 @@ class json_pointer return std::isdigit(x); }); - // change value to array for numbers or "-" or to object otherwise + // change value to an array for numbers or "-" or to object otherwise *ptr = (nums || reference_token == "-") ? detail::value_t::array : detail::value_t::object; @@ -609,7 +609,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9'))) { - // first char should be between '1' and '9' + // the first char should be between '1' and '9' return false; } for (std::size_t i = 1; i < reference_token.size(); i++) @@ -673,7 +673,7 @@ class json_pointer return result; } - // check if nonempty reference string begins with slash + // check if a nonempty reference string begins with slash if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) { JSON_THROW(detail::parse_error::create(107, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr)); @@ -747,10 +747,10 @@ class json_pointer } else { - // iterate array and use index as reference string + // iterate array and use index as a reference string for (std::size_t i = 0; i < value.m_data.m_value.array->size(); ++i) { - flatten(detail::concat(reference_string, '/', std::to_string(i)), + flatten(detail::concat(reference_string, '/', std::to_string(i)), value.m_data.m_value.array->operator[](i), result); } } @@ -769,7 +769,7 @@ class json_pointer // iterate object and use keys as reference string for (const auto& element : *value.m_data.m_value.object) { - flatten(detail::concat(reference_string, '/', detail::escape(element.first)), element.second, result); + flatten(detail::concat(reference_string, '/', detail::escape(element.first)), element.second, result); } } break; @@ -785,7 +785,7 @@ class json_pointer case detail::value_t::discarded: default: { - // add primitive value with its reference string + // add a primitive value with its reference string result[reference_string] = value; break; } @@ -821,17 +821,17 @@ class json_pointer JSON_THROW(detail::type_error::create(315, "values in object must be primitive", &element.second)); } - // assign value to reference pointed to by JSON pointer; Note that if - // the JSON pointer is "" (i.e., points to the whole value), function - // get_and_create returns a reference to result itself. An assignment - // will then create a primitive value. + // Assign the value to the reference pointed to by JSON pointer. Note + // that if the JSON pointer is "" (i.e., points to the whole value), + // function get_and_create returns a reference to the result itself. + // An assignment will then create a primitive value. json_pointer(element.first).get_and_create(result) = element.second; } return result; } - // can't use conversion operator because of ambiguity + // can't use the conversion operator because of ambiguity json_pointer convert() const& { json_pointer result; @@ -926,7 +926,7 @@ class json_pointer }; #if !JSON_HAS_THREE_WAY_COMPARISON -// functions cannot be defined inside class due to ODR violations +// functions cannot be defined inside the class due to ODR violations template inline bool operator==(const json_pointer& lhs, const json_pointer& rhs) noexcept diff --git a/include/nlohmann/detail/json_ref.hpp b/include/nlohmann/detail/json_ref.hpp index b8bb6a76b0..0d52b0a628 100644 --- a/include/nlohmann/detail/json_ref.hpp +++ b/include/nlohmann/detail/json_ref.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 97127a6462..afb400c90b 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once @@ -31,16 +31,27 @@ #endif // C++ language standard detection -// if the user manually specified the used c++ version this is skipped -#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) - #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) +// if the user manually specified the used C++ version, this is skipped +#if !defined(JSON_HAS_CPP_26) && !defined(JSON_HAS_CPP_23) && !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) + #if (defined(__cplusplus) && __cplusplus > 202302L) || (defined(_MSVC_LANG) && _MSVC_LANG > 202302L) + #define JSON_HAS_CPP_26 + #define JSON_HAS_CPP_23 #define JSON_HAS_CPP_20 #define JSON_HAS_CPP_17 #define JSON_HAS_CPP_14 - #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #elif (defined(__cplusplus) && __cplusplus > 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG > 202002L) + #define JSON_HAS_CPP_23 + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus > 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG > 201703L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus > 201402L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 #define JSON_HAS_CPP_17 #define JSON_HAS_CPP_14 - #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #elif (defined(__cplusplus) && __cplusplus > 201103L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) #define JSON_HAS_CPP_14 #endif // the cpp 11 flag is always specified because it is the minimal required version @@ -123,7 +134,7 @@ #endif #ifndef JSON_HAS_RANGES - // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error + // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has a syntax error #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427 #define JSON_HAS_RANGES 0 #elif defined(__cpp_lib_ranges) @@ -200,7 +211,7 @@ #define JSON_ASSERT(x) assert(x) #endif -// allow to access some private functions (needed by the test suite) +// allow accessing some private functions (needed by the test suite) #if defined(JSON_TESTS_PRIVATE) #define JSON_PRIVATE_UNLESS_TESTED public #else @@ -216,7 +227,9 @@ template \ inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ { \ + /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \ static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + /* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on */ \ static const std::pair m[] = __VA_ARGS__; \ auto it = std::find_if(std::begin(m), std::end(m), \ [e](const std::pair& ej_pair) -> bool \ @@ -228,7 +241,9 @@ template \ inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ { \ + /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \ static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + /* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on */ \ static const std::pair m[] = __VA_ARGS__; \ auto it = std::find_if(std::begin(m), std::end(m), \ [&j](const std::pair& ej_pair) -> bool \ @@ -391,42 +406,146 @@ #define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; #define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); -#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); +#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = !nlohmann_json_j.is_null() ? nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1) : nlohmann_json_default_obj.v1; /*! @brief macro @def NLOHMANN_DEFINE_TYPE_INTRUSIVE @since version 3.9.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/ */ #define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ - friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + template::value, int> = 0> \ + friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT +@since version 3.11.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/ +*/ #define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ - friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + template::value, int> = 0> \ + friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE +@since version 3.11.3 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/ +*/ #define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ - friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } + template::value, int> = 0> \ + friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } /*! @brief macro @def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE @since version 3.9.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/ */ #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ - inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + template::value, int> = 0> \ + void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT +@since version 3.11.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/ +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + template::value, int> = 0> \ + void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE +@since version 3.11.3 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/ +*/ #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ - inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } + template::value, int> = 0> \ + void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } -#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ - inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } +/*! +@brief macro +@def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE +@since version 3.12.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ +*/ +#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(Type, BaseType, ...) \ + template::value, int> = 0> \ + friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT +@since version 3.12.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ +*/ +#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \ + template::value, int> = 0> \ + friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE +@since version 3.12.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ +*/ +#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \ + template::value, int> = 0> \ + friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE +@since version 3.12.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ +*/ +#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(Type, BaseType, ...) \ + template::value, int> = 0> \ + void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT +@since version 3.12.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ +*/ +#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \ + template::value, int> = 0> \ + void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE +@since version 3.12.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ +*/ +#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \ + template::value, int> = 0> \ + void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } // inspired from https://stackoverflow.com/a/26745591 -// allows to call any std function as if (e.g. with begin): +// allows calling any std function as if (e.g., with begin): // using std::begin; begin(x); // // it allows using the detected idiom to retrieve the return type diff --git a/include/nlohmann/detail/macro_unscope.hpp b/include/nlohmann/detail/macro_unscope.hpp index c6620d1e2c..af6d8370cb 100644 --- a/include/nlohmann/detail/macro_unscope.hpp +++ b/include/nlohmann/detail/macro_unscope.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once @@ -34,6 +34,8 @@ #undef JSON_HAS_CPP_14 #undef JSON_HAS_CPP_17 #undef JSON_HAS_CPP_20 + #undef JSON_HAS_CPP_23 + #undef JSON_HAS_CPP_26 #undef JSON_HAS_FILESYSTEM #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM #undef JSON_HAS_THREE_WAY_COMPARISON diff --git a/include/nlohmann/detail/meta/call_std/begin.hpp b/include/nlohmann/detail/meta/call_std/begin.hpp index 364cc89d87..a086a5f3d3 100644 --- a/include/nlohmann/detail/meta/call_std/begin.hpp +++ b/include/nlohmann/detail/meta/call_std/begin.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once diff --git a/include/nlohmann/detail/meta/call_std/end.hpp b/include/nlohmann/detail/meta/call_std/end.hpp index 463f07061b..40c9942b98 100644 --- a/include/nlohmann/detail/meta/call_std/end.hpp +++ b/include/nlohmann/detail/meta/call_std/end.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once diff --git a/include/nlohmann/detail/meta/cpp_future.hpp b/include/nlohmann/detail/meta/cpp_future.hpp index 412b5aa74b..95bd79a9b7 100644 --- a/include/nlohmann/detail/meta/cpp_future.hpp +++ b/include/nlohmann/detail/meta/cpp_future.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-FileCopyrightText: 2018 The Abseil Authors // SPDX-License-Identifier: MIT @@ -162,7 +162,7 @@ struct static_const #endif template -inline constexpr std::array make_array(Args&& ... args) +constexpr std::array make_array(Args&& ... args) { return std::array {{static_cast(std::forward(args))...}}; } diff --git a/include/nlohmann/detail/meta/detected.hpp b/include/nlohmann/detail/meta/detected.hpp index 1db9bf9ca3..8da22eb9bf 100644 --- a/include/nlohmann/detail/meta/detected.hpp +++ b/include/nlohmann/detail/meta/detected.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once diff --git a/include/nlohmann/detail/meta/identity_tag.hpp b/include/nlohmann/detail/meta/identity_tag.hpp index 269deffb26..482f3ea6ee 100644 --- a/include/nlohmann/detail/meta/identity_tag.hpp +++ b/include/nlohmann/detail/meta/identity_tag.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once diff --git a/include/nlohmann/detail/meta/is_sax.hpp b/include/nlohmann/detail/meta/is_sax.hpp index 4e02bc1480..8e8a0de241 100644 --- a/include/nlohmann/detail/meta/is_sax.hpp +++ b/include/nlohmann/detail/meta/is_sax.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once diff --git a/include/nlohmann/detail/meta/std_fs.hpp b/include/nlohmann/detail/meta/std_fs.hpp index fd18039649..b86d5376c6 100644 --- a/include/nlohmann/detail/meta/std_fs.hpp +++ b/include/nlohmann/detail/meta/std_fs.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once @@ -19,7 +19,7 @@ namespace std_fs = std::experimental::filesystem; } // namespace detail NLOHMANN_JSON_NAMESPACE_END #elif JSON_HAS_FILESYSTEM -#include +#include // NOLINT(build/c++17) NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index e1b000dcc2..4a1f346f96 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -1,19 +1,21 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once #include // numeric_limits +#include // char_traits +#include // tuple #include // false_type, is_constructible, is_integral, is_same, true_type #include // declval -#include // tuple -#include // char_traits - +#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L + #include // byte +#endif #include #include #include @@ -40,12 +42,12 @@ namespace detail // Note to maintainers: // -// Every trait in this file expects a non CV-qualified type. +// Every trait in this file expects a non-CV-qualified type. // The only exceptions are in the 'aliases for detected' section -// (i.e. those of the form: decltype(T::member_function(std::declval()))) +// (i.e., those of the form: decltype(T::member_function(std::declval()))) // // In this case, T has to be properly CV-qualified to constraint the function arguments -// (e.g. to_json(BasicJsonType&, const T&)) +// (e.g., to_json(BasicJsonType&, const T&)) template struct is_basic_json : std::false_type {}; @@ -53,7 +55,7 @@ NLOHMANN_BASIC_JSON_TPL_DECLARATION struct is_basic_json : std::true_type {}; // used by exceptions create() member functions -// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t +// true_type for the pointer to possibly cv-qualified basic_json or std::nullptr_t // false_type otherwise template struct is_basic_json_context : @@ -211,7 +213,7 @@ struct char_traits : std::char_traits static constexpr int_type eof() noexcept { - return static_cast(EOF); + return static_cast(std::char_traits::eof()); } }; @@ -235,10 +237,34 @@ struct char_traits : std::char_traits static constexpr int_type eof() noexcept { - return static_cast(EOF); + return static_cast(std::char_traits::eof()); } }; +#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L +template<> +struct char_traits : std::char_traits +{ + using char_type = std::byte; + using int_type = uint64_t; + + static int_type to_int_type(char_type c) noexcept + { + return static_cast(std::to_integer(c)); + } + + static char_type to_char_type(int_type i) noexcept + { + return std::byte(static_cast(i)); + } + + static constexpr int_type eof() noexcept + { + return static_cast(std::char_traits::eof()); + } +}; +#endif + /////////////////// // is_ functions // /////////////////// @@ -255,25 +281,25 @@ template struct negation : std::integral_constant < bool, !B::value > { // Reimplementation of is_constructible and is_default_constructible, due to them being broken for // std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). -// This causes compile errors in e.g. clang 3.5 or gcc 4.9. +// This causes compile errors in e.g., Clang 3.5 or GCC 4.9. template struct is_default_constructible : std::is_default_constructible {}; template struct is_default_constructible> - : conjunction, is_default_constructible> {}; + : conjunction, is_default_constructible> {}; template struct is_default_constructible> - : conjunction, is_default_constructible> {}; + : conjunction, is_default_constructible> {}; template struct is_default_constructible> - : conjunction...> {}; + : conjunction...> {}; template struct is_default_constructible> - : conjunction...> {}; + : conjunction...> {}; template struct is_constructible : std::is_constructible {}; @@ -335,7 +361,7 @@ using range_value_t = value_type_t>>; // The following implementation of is_complete_type is taken from // https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ -// and is written by Xiang Fan who agreed to using it in this library. +// and is written by Xiang Fan who agreed to use it in this library. template struct is_complete_type : std::false_type {}; @@ -471,8 +497,8 @@ is_detected::value&& // special case for types like std::filesystem::path whose iterator's value_type are themselves // c.f. https://github.com/nlohmann/json/pull/3073 !std::is_same>::value&& - is_complete_type < - detected_t>::value >> +is_complete_type < +detected_t>::value >> { using value_type = range_value_t; @@ -559,20 +585,35 @@ struct is_specialization_of> : std::true_type {}; template using is_json_pointer = is_specialization_of<::nlohmann::json_pointer, uncvref_t>; +// checks if B is a json_pointer +template +struct is_json_pointer_of : std::false_type {}; + +template +struct is_json_pointer_of> : std::true_type {}; + +template +struct is_json_pointer_of&> : std::true_type {}; + // checks if A and B are comparable using Compare functor template struct is_comparable : std::false_type {}; +// We exclude json_pointer here, because the checks using Compare(A, B) will +// use json_pointer::operator string_t() which triggers a deprecation warning +// for GCC. See https://github.com/nlohmann/json/issues/4621. The call to +// is_json_pointer_of can be removed once the deprecated function has been +// removed. template -struct is_comparable()(std::declval(), std::declval())), -decltype(std::declval()(std::declval(), std::declval())) +struct is_comparable < Compare, A, B, enable_if_t < !is_json_pointer_of::value +&& std::is_constructible ()(std::declval(), std::declval()))>::value +&& std::is_constructible ()(std::declval(), std::declval()))>::value >> : std::true_type {}; template using detect_is_transparent = typename T::is_transparent; -// type trait to check if KeyType can be used as object key (without a BasicJsonType) +// type trait to check if KeyType can be used as an object key (without a BasicJsonType) // see is_usable_as_basic_json_key_type below template> @@ -586,7 +627,7 @@ using is_usable_as_key_type = typename std::conditional < std::true_type, std::false_type >::type; -// type trait to check if KeyType can be used as object key +// type trait to check if KeyType can be used as an object key // true if: // - KeyType is comparable with BasicJsonType::object_t::key_type // - if ExcludeObjectKeyType is true, KeyType is not BasicJsonType::object_t::key_type @@ -595,12 +636,12 @@ using is_usable_as_key_type = typename std::conditional < template> using is_usable_as_basic_json_key_type = typename std::conditional < - is_usable_as_key_type::value - && !is_json_iterator_of::value, - std::true_type, - std::false_type >::type; + is_usable_as_key_type::value + && !is_json_iterator_of::value, + std::true_type, + std::false_type >::type; template using detect_erase_with_key_type = decltype(std::declval().erase(std::declval())); @@ -629,7 +670,7 @@ struct is_ordered_map template static one test( decltype(&C::capacity) ) ; template static two test(...); - enum { value = sizeof(test(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + enum { value = sizeof(test(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg,cppcoreguidelines-use-enum-class) }; // to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324) @@ -734,7 +775,7 @@ struct value_in_range_of_impl1 }; template -inline constexpr bool value_in_range_of(T val) +constexpr bool value_in_range_of(T val) { return value_in_range_of_impl1::test(val); } @@ -750,7 +791,7 @@ namespace impl { template -inline constexpr bool is_c_string() +constexpr bool is_c_string() { using TUnExt = typename std::remove_extent::type; using TUnCVExt = typename std::remove_cv::type; @@ -778,7 +819,7 @@ namespace impl { template -inline constexpr bool is_transparent() +constexpr bool is_transparent() { return is_detected::value; } diff --git a/include/nlohmann/detail/meta/void_t.hpp b/include/nlohmann/detail/meta/void_t.hpp index 99615c7c5a..40fda6a612 100644 --- a/include/nlohmann/detail/meta/void_t.hpp +++ b/include/nlohmann/detail/meta/void_t.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index f475d57be8..d0c947bba9 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once @@ -28,6 +28,13 @@ NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { +/// how to encode BJData +enum class bjdata_version_t +{ + draft2, + draft3, +}; + /////////////////// // binary writer // /////////////////// @@ -612,7 +619,7 @@ class binary_writer case value_t::binary: { // step 0: determine if the binary type has a set subtype to - // determine whether or not to use the ext or fixext types + // determine whether to use the ext or fixext types const bool use_ext = j.m_data.m_value.binary->has_subtype(); // step 1: write control byte and the byte string length @@ -735,11 +742,14 @@ class binary_writer @param[in] use_type whether to use '$' prefixes (optimized format) @param[in] add_prefix whether prefixes need to be used for this value @param[in] use_bjdata whether write in BJData format, default is false + @param[in] bjdata_version which BJData version to use, default is draft2 */ void write_ubjson(const BasicJsonType& j, const bool use_count, const bool use_type, const bool add_prefix = true, - const bool use_bjdata = false) + const bool use_bjdata = false, const bjdata_version_t bjdata_version = bjdata_version_t::draft2) { + const bool bjdata_draft3 = use_bjdata && bjdata_version == bjdata_version_t::draft3; + switch (j.type()) { case value_t::null: @@ -829,7 +839,7 @@ class binary_writer for (const auto& el : *j.m_data.m_value.array) { - write_ubjson(el, use_count, use_type, prefix_required, use_bjdata); + write_ubjson(el, use_count, use_type, prefix_required, use_bjdata, bjdata_version); } if (!use_count) @@ -847,11 +857,11 @@ class binary_writer oa->write_character(to_char_type('[')); } - if (use_type && !j.m_data.m_value.binary->empty()) + if (use_type && (bjdata_draft3 || !j.m_data.m_value.binary->empty())) { JSON_ASSERT(use_count); oa->write_character(to_char_type('$')); - oa->write_character('U'); + oa->write_character(bjdata_draft3 ? 'B' : 'U'); } if (use_count) @@ -870,7 +880,7 @@ class binary_writer { for (size_t i = 0; i < j.m_data.m_value.binary->size(); ++i) { - oa->write_character(to_char_type('U')); + oa->write_character(to_char_type(bjdata_draft3 ? 'B' : 'U')); oa->write_character(j.m_data.m_value.binary->data()[i]); } } @@ -887,7 +897,7 @@ class binary_writer { if (use_bjdata && j.m_data.m_value.object->size() == 3 && j.m_data.m_value.object->find("_ArrayType_") != j.m_data.m_value.object->end() && j.m_data.m_value.object->find("_ArraySize_") != j.m_data.m_value.object->end() && j.m_data.m_value.object->find("_ArrayData_") != j.m_data.m_value.object->end()) { - if (!write_bjdata_ndarray(*j.m_data.m_value.object, use_count, use_type)) // decode bjdata ndarray in the JData format (https://github.com/NeuroJSON/jdata) + if (!write_bjdata_ndarray(*j.m_data.m_value.object, use_count, use_type, bjdata_version)) // decode bjdata ndarray in the JData format (https://github.com/NeuroJSON/jdata) { break; } @@ -931,7 +941,7 @@ class binary_writer oa->write_characters( reinterpret_cast(el.first.c_str()), el.first.size()); - write_ubjson(el.second, use_count, use_type, prefix_required, use_bjdata); + write_ubjson(el.second, use_count, use_type, prefix_required, use_bjdata, bjdata_version); } if (!use_count) @@ -963,9 +973,9 @@ class binary_writer if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) { JSON_THROW(out_of_range::create(409, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j)); - static_cast(j); } + static_cast(j); return /*id*/ 1ul + name.size() + /*zero-terminator*/1u; } @@ -1087,7 +1097,8 @@ class binary_writer } else { - JSON_THROW(out_of_range::create(407, concat("integer number ", std::to_string(j.m_data.m_value.number_unsigned), " cannot be represented by BSON as it does not fit int64"), &j)); + write_bson_entry_header(name, 0x11 /* uint64 */); + write_number(static_cast(j.m_data.m_value.number_unsigned), true); } } @@ -1541,7 +1552,7 @@ class binary_writer { return 'L'; } - // anything else is treated as high-precision number + // anything else is treated as a high-precision number return 'H'; // LCOV_EXCL_LINE } @@ -1579,7 +1590,7 @@ class binary_writer { return 'M'; } - // anything else is treated as high-precision number + // anything else is treated as a high-precision number return 'H'; // LCOV_EXCL_LINE } @@ -1615,10 +1626,11 @@ class binary_writer /*! @return false if the object is successfully converted to a bjdata ndarray, true if the type or size is invalid */ - bool write_bjdata_ndarray(const typename BasicJsonType::object_t& value, const bool use_count, const bool use_type) + bool write_bjdata_ndarray(const typename BasicJsonType::object_t& value, const bool use_count, const bool use_type, const bjdata_version_t bjdata_version) { std::map bjdtype = {{"uint8", 'U'}, {"int8", 'i'}, {"uint16", 'u'}, {"int16", 'I'}, - {"uint32", 'm'}, {"int32", 'l'}, {"uint64", 'M'}, {"int64", 'L'}, {"single", 'd'}, {"double", 'D'}, {"char", 'C'} + {"uint32", 'm'}, {"int32", 'l'}, {"uint64", 'M'}, {"int64", 'L'}, {"single", 'd'}, {"double", 'D'}, + {"char", 'C'}, {"byte", 'B'} }; string_t key = "_ArrayType_"; @@ -1648,10 +1660,10 @@ class binary_writer oa->write_character('#'); key = "_ArraySize_"; - write_ubjson(value.at(key), use_count, use_type, true, true); + write_ubjson(value.at(key), use_count, use_type, true, true, bjdata_version); key = "_ArrayData_"; - if (dtype == 'U' || dtype == 'C') + if (dtype == 'U' || dtype == 'C' || dtype == 'B') { for (const auto& el : value.at(key)) { @@ -1744,11 +1756,11 @@ class binary_writer template void write_number(const NumberType n, const bool OutputIsLittleEndian = false) { - // step 1: write number to array of length NumberType + // step 1: write the number to an array of length NumberType std::array vec{}; std::memcpy(vec.data(), &n, sizeof(NumberType)); - // step 2: write array to output (with possible reordering) + // step 2: write the array to output (with possible reordering) if (is_little_endian != OutputIsLittleEndian) { // reverse byte order prior to conversion if necessary @@ -1764,9 +1776,9 @@ class binary_writer #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wfloat-equal" #endif - if (static_cast(n) >= static_cast(std::numeric_limits::lowest()) && - static_cast(n) <= static_cast((std::numeric_limits::max)()) && - static_cast(static_cast(n)) == static_cast(n)) + if (!std::isfinite(n) || ((static_cast(n) >= static_cast(std::numeric_limits::lowest()) && + static_cast(n) <= static_cast((std::numeric_limits::max)()) && + static_cast(static_cast(n)) == static_cast(n)))) { oa->write_character(format == detail::input_format_t::cbor ? get_cbor_float_prefix(static_cast(n)) @@ -1801,8 +1813,21 @@ class binary_writer enable_if_t < std::is_signed::value && std::is_unsigned::value > * = nullptr > static CharType to_char_type(std::uint8_t x) noexcept { - static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t"); + // The std::is_trivial trait is deprecated in C++26. The replacement is to use + // std::is_trivially_copyable and std::is_trivially_default_constructible. + // However, some older library implementations support std::is_trivial + // but not all the std::is_trivially_* traits. + // Since detecting full support across all libraries is difficult, + // we use std::is_trivial unless we are using a standard where it has been deprecated. + // For more details, see: https://github.com/nlohmann/json/pull/4775#issuecomment-2884361627 +#ifdef JSON_HAS_CPP_26 + static_assert(std::is_trivially_copyable::value, "CharType must be trivially copyable"); + static_assert(std::is_trivially_default_constructible::value, "CharType must be trivially default constructible"); +#else static_assert(std::is_trivial::value, "CharType must be trivial"); +#endif + + static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t"); CharType result; std::memcpy(&result, &x, sizeof(x)); return result; diff --git a/include/nlohmann/detail/output/output_adapters.hpp b/include/nlohmann/detail/output/output_adapters.hpp index 626f7c0c85..94763e64a7 100644 --- a/include/nlohmann/detail/output/output_adapters.hpp +++ b/include/nlohmann/detail/output/output_adapters.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index ed20b0d9e1..03d6ba352f 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -1,10 +1,10 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2008-2009 Björn Hoehrmann -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2008, 2009 Björn Hoehrmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once @@ -75,7 +75,7 @@ class serializer , error_handler(error_handler_) {} - // delete because of pointer members + // deleted because of pointer members serializer(const serializer&) = delete; serializer& operator=(const serializer&) = delete; serializer(serializer&&) = delete; @@ -573,7 +573,7 @@ class serializer break; } - default: // decode found yet incomplete multi-byte code point + default: // decode found yet incomplete multibyte code point { if (!ensure_ascii) { @@ -643,7 +643,7 @@ class serializer @param[in] x unsigned integer number to count its digits @return number of decimal digits */ - inline unsigned int count_digits(number_unsigned_t x) noexcept + unsigned int count_digits(number_unsigned_t x) noexcept { unsigned int n_digits = 1; for (;;) @@ -762,7 +762,7 @@ class serializer // jump to the end to generate the string from backward, // so we later avoid reversing the result - buffer_ptr += n_chars; + buffer_ptr += static_cast(n_chars); // Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu // See: https://www.youtube.com/watch?v=o4-CwDo2zpg @@ -827,7 +827,7 @@ class serializer void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/) { - // get number of digits for a float -> text -> float round-trip + // get the number of digits for a float -> text -> float round-trip static constexpr auto d = std::numeric_limits::max_digits10; // the actual conversion @@ -836,10 +836,10 @@ class serializer // negative value indicates an error JSON_ASSERT(len > 0); - // check if buffer was large enough + // check if the buffer was large enough JSON_ASSERT(static_cast(len) < number_buffer.size()); - // erase thousands separator + // erase thousands separators if (thousands_sep != '\0') { // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081 @@ -926,7 +926,7 @@ class serializer ? (byte & 0x3fu) | (codep << 6u) : (0xFFu >> type) & (byte); - const std::size_t index = 256u + static_cast(state) * 16u + static_cast(type); + const std::size_t index = 256u + (static_cast(state) * 16u) + static_cast(type); JSON_ASSERT(index < utf8d.size()); state = utf8d[index]; return state; @@ -947,12 +947,12 @@ class serializer * Helper function for dump_integer * * This function takes a negative signed integer and returns its absolute - * value as unsigned integer. The plus/minus shuffling is necessary as we can - * not directly remove the sign of an arbitrary signed integer as the + * value as an unsigned integer. The plus/minus shuffling is necessary as we + * cannot directly remove the sign of an arbitrary signed integer as the * absolute values of INT_MIN and INT_MAX are usually not the same. See * #1708 for details. */ - inline number_unsigned_t remove_sign(number_integer_t x) noexcept + number_unsigned_t remove_sign(number_integer_t x) noexcept { JSON_ASSERT(x < 0 && x < (std::numeric_limits::max)()); // NOLINT(misc-redundant-expression) return static_cast(-(x + 1)) + 1; diff --git a/include/nlohmann/detail/string_concat.hpp b/include/nlohmann/detail/string_concat.hpp index f49e8d215e..a39dd5e637 100644 --- a/include/nlohmann/detail/string_concat.hpp +++ b/include/nlohmann/detail/string_concat.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once diff --git a/include/nlohmann/detail/string_escape.hpp b/include/nlohmann/detail/string_escape.hpp index 7f1b5c562b..7715dde7a3 100644 --- a/include/nlohmann/detail/string_escape.hpp +++ b/include/nlohmann/detail/string_escape.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once @@ -32,10 +32,10 @@ inline void replace_substring(StringType& s, const StringType& f, const StringType& t) { JSON_ASSERT(!f.empty()); - for (auto pos = s.find(f); // find first occurrence of f + for (auto pos = s.find(f); // find the first occurrence of f pos != StringType::npos; // make sure f was found s.replace(pos, f.size(), t), // replace with t, and - pos = s.find(f, pos + t.size())) // find next occurrence of f + pos = s.find(f, pos + t.size())) // find the next occurrence of f {} } @@ -62,7 +62,7 @@ inline StringType escape(StringType s) * Note the order of escaping "~1" to "/" and "~0" to "~" is important. */ template -static void unescape(StringType& s) +inline void unescape(StringType& s) { replace_substring(s, StringType{"~1"}, StringType{"/"}); replace_substring(s, StringType{"~0"}, StringType{"~"}); diff --git a/include/nlohmann/detail/string_utils.hpp b/include/nlohmann/detail/string_utils.hpp new file mode 100644 index 0000000000..fe2f9109dd --- /dev/null +++ b/include/nlohmann/detail/string_utils.hpp @@ -0,0 +1,37 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + +#pragma once + +#include // size_t +#include // string, to_string + +#include + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template +void int_to_string(StringType& target, std::size_t value) +{ + // For ADL + using std::to_string; + target = to_string(value); +} + +template +StringType to_string(std::size_t value) +{ + StringType result; + int_to_string(result, value); + return result; +} + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END diff --git a/include/nlohmann/detail/value_t.hpp b/include/nlohmann/detail/value_t.hpp index 07688fe8c6..06aefa5acd 100644 --- a/include/nlohmann/detail/value_t.hpp +++ b/include/nlohmann/detail/value_t.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 31ca64539b..0b8f155ac5 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT /****************************************************************************\ @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -114,9 +115,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec friend class ::nlohmann::detail::binary_writer; template friend class ::nlohmann::detail::binary_reader; - template + template friend class ::nlohmann::detail::json_sax_dom_parser; - template + template friend class ::nlohmann::detail::json_sax_dom_callback_parser; friend class ::nlohmann::detail::exception; @@ -133,11 +134,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec InputAdapterType adapter, detail::parser_callback_tcb = 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(std::move(adapter), - std::move(cb), allow_exceptions, ignore_comments); + std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas); } private: @@ -170,6 +172,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec using error_handler_t = detail::error_handler_t; /// how to treat CBOR tags using cbor_tag_handler_t = detail::cbor_tag_handler_t; + /// how to encode BJData + using bjdata_version_t = detail::bjdata_version_t; /// helper type for initializer lists of basic_json values using initializer_list_t = std::initializer_list>; @@ -249,7 +253,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { basic_json result; - result["copyright"] = "(C) 2013-2023 Niels Lohmann"; + result["copyright"] = "(C) 2013-2026 Niels Lohmann"; result["name"] = "JSON for Modern C++"; result["url"] = "https://github.com/nlohmann/json"; result["version"]["string"] = @@ -514,7 +518,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec object = nullptr; // silence warning, see #821 if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) { - JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.11.3", nullptr)); // LCOV_EXCL_LINE + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.12.0", nullptr)); // LCOV_EXCL_LINE } break; } @@ -560,7 +564,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec (t == value_t::binary && binary == nullptr) ) { - //not initialized (e.g. due to exception in the ctor) + // not initialized (e.g., due to exception in the ctor) return; } if (t == value_t::array || t == value_t::object) @@ -585,7 +589,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec while (!stack.empty()) { - // move the last item to local variable to be processed + // move the last item to a local variable to be processed basic_json current_item(std::move(stack.back())); stack.pop_back(); @@ -607,7 +611,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec current_item.m_data.m_value.object->clear(); } - // it's now safe that current_item get destructed + // it's now safe that current_item gets destructed // since it doesn't have any children } } @@ -750,10 +754,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return it; } - reference set_parent(reference j, std::size_t old_capacity = static_cast(-1)) + reference set_parent(reference j, std::size_t old_capacity = detail::unknown_size()) { #if JSON_DIAGNOSTICS - if (old_capacity != static_cast(-1)) + if (old_capacity != detail::unknown_size()) { // see https://github.com/nlohmann/json/issues/2838 JSON_ASSERT(type() == value_t::array); @@ -847,6 +851,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::enable_if_t < detail::is_basic_json::value&& !std::is_same::value, int > = 0 > basic_json(const BasicJsonType& val) +#if JSON_DIAGNOSTIC_POSITIONS + : start_position(val.start_pos()), + end_position(val.end_pos()) +#endif { using other_boolean_t = typename BasicJsonType::boolean_t; using other_number_float_t = typename BasicJsonType::number_float_t; @@ -893,6 +901,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE } JSON_ASSERT(m_data.m_type == val.type()); + set_parents(); assert_invariant(); } @@ -910,20 +919,20 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { // The cast is to ensure op[size_type] is called, bearing in mind size_type may not be int; // (many string types can be constructed from 0 via its null-pointer guise, so we get a - // broken call to op[key_type], the wrong semantics and a 4804 warning on Windows) + // broken call to op[key_type], the wrong semantics, and a 4804 warning on Windows) return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[static_cast(0)].is_string(); }); // adjust type if type deduction is not wanted if (!type_deduction) { - // if array is wanted, do not create an object though possible + // if an array is wanted, do not create an object though possible if (manual_type == value_t::array) { is_an_object = false; } - // if object is wanted but impossible, throw an exception + // if an object is wanted but impossible, throw an exception if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object)) { JSON_THROW(type_error::create(301, "cannot create object from initializer list", nullptr)); @@ -932,7 +941,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (is_an_object) { - // the initializer list is a list of pairs -> create object + // the initializer list is a list of pairs -> create an object m_data.m_type = value_t::object; m_data.m_value = value_t::object; @@ -946,7 +955,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } else { - // the initializer list describes an array -> create array + // the initializer list describes an array -> create an array m_data.m_type = value_t::array; m_data.m_value.array = create(init.begin(), init.end()); } @@ -1029,21 +1038,21 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec template < class InputIT, typename std::enable_if < std::is_same::value || std::is_same::value, int >::type = 0 > - basic_json(InputIT first, InputIT last) + basic_json(InputIT first, InputIT last) // NOLINT(performance-unnecessary-value-param) { JSON_ASSERT(first.m_object != nullptr); JSON_ASSERT(last.m_object != nullptr); - // make sure iterator fits the current value + // make sure the iterator fits the current value if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", nullptr)); } - // copy type from first iterator + // copy type from the first iterator m_data.m_type = first.m_object->m_data.m_type; - // check if iterator range is complete for primitive values + // check if the iterator range is complete for primitive values switch (m_data.m_type) { case value_t::boolean: @@ -1144,6 +1153,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ basic_json(const basic_json& other) : json_base_class_t(other) +#if JSON_DIAGNOSTIC_POSITIONS + , start_position(other.start_position) + , end_position(other.end_position) +#endif { m_data.m_type = other.m_data.m_type; // check of passed value is valid @@ -1213,22 +1226,31 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ basic_json(basic_json&& other) noexcept : json_base_class_t(std::forward(other)), - m_data(std::move(other.m_data)) + m_data(std::move(other.m_data)) // cppcheck-suppress[accessForwarded] TODO check +#if JSON_DIAGNOSTIC_POSITIONS + , start_position(other.start_position) // cppcheck-suppress[accessForwarded] TODO check + , end_position(other.end_position) // cppcheck-suppress[accessForwarded] TODO check +#endif { - // check that passed value is valid - other.assert_invariant(false); + // check that the passed value is valid + other.assert_invariant(false); // cppcheck-suppress[accessForwarded] // invalidate payload other.m_data.m_type = value_t::null; other.m_data.m_value = {}; +#if JSON_DIAGNOSTIC_POSITIONS + other.start_position = std::string::npos; + other.end_position = std::string::npos; +#endif + set_parents(); assert_invariant(); } /// @brief copy assignment /// @sa https://json.nlohmann.me/api/basic_json/operator=/ - basic_json& operator=(basic_json other) noexcept ( + basic_json& operator=(basic_json other) noexcept ( // NOLINT(cppcoreguidelines-c-copy-assignment-signature,misc-unconventional-assign-operator) std::is_nothrow_move_constructible::value&& std::is_nothrow_move_assignable::value&& std::is_nothrow_move_constructible::value&& @@ -1236,12 +1258,18 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec std::is_nothrow_move_assignable::value ) { - // check that passed value is valid + // check that the passed value is valid other.assert_invariant(); using std::swap; swap(m_data.m_type, other.m_data.m_type); swap(m_data.m_value, other.m_data.m_value); + +#if JSON_DIAGNOSTIC_POSITIONS + swap(start_position, other.start_position); + swap(end_position, other.end_position); +#endif + json_base_class_t::operator=(std::move(other)); set_parents(); @@ -1463,13 +1491,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// get a pointer to the value (integer number) number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept { - return is_number_integer() ? &m_data.m_value.number_integer : nullptr; + return m_data.m_type == value_t::number_integer ? &m_data.m_value.number_integer : nullptr; } /// get a pointer to the value (integer number) constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept { - return is_number_integer() ? &m_data.m_value.number_integer : nullptr; + return m_data.m_type == value_t::number_integer ? &m_data.m_value.number_integer : nullptr; } /// get a pointer to the value (unsigned number) @@ -1946,9 +1974,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } JSON_CATCH (std::out_of_range&) { - // create better exception explanation + // create a better exception explanation JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this)); - } + } // cppcheck-suppress[missingReturn] } else { @@ -1969,9 +1997,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } JSON_CATCH (std::out_of_range&) { - // create better exception explanation + // create a better exception explanation JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this)); - } + } // cppcheck-suppress[missingReturn] } else { @@ -2059,7 +2087,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ reference operator[](size_type idx) { - // implicitly convert null value to an empty array + // implicitly convert a null value to an empty array if (is_null()) { m_data.m_type = value_t::array; @@ -2070,7 +2098,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // operator[] only works for arrays if (JSON_HEDLEY_LIKELY(is_array())) { - // fill up array with null values if given idx is outside range + // fill up the array with null values if given idx is outside the range if (idx >= m_data.m_value.array->size()) { #if JSON_DIAGNOSTICS @@ -2116,9 +2144,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief access specified object element /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ - reference operator[](typename object_t::key_type key) + reference operator[](typename object_t::key_type key) // NOLINT(performance-unnecessary-value-param) { - // implicitly convert null value to an empty object + // implicitly convert a null value to an empty object if (is_null()) { m_data.m_type = value_t::object; @@ -2171,7 +2199,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::is_usable_as_basic_json_key_type::value, int > = 0 > reference operator[](KeyType && key) { - // implicitly convert null value to an empty object + // implicitly convert a null value to an empty object if (is_null()) { m_data.m_type = value_t::object; @@ -2228,7 +2256,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // value only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - // if key is found, return value and given default value otherwise + // If 'key' is found, return its value. Otherwise, return `default_value'. const auto it = find(key); if (it != end()) { @@ -2253,7 +2281,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // value only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - // if key is found, return value and given default value otherwise + // If 'key' is found, return its value. Otherwise, return `default_value'. const auto it = find(key); if (it != end()) { @@ -2279,7 +2307,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // value only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - // if key is found, return value and given default value otherwise + // If 'key' is found, return its value. Otherwise, return `default_value'. const auto it = find(std::forward(key)); if (it != end()) { @@ -2306,7 +2334,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // value only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - // if key is found, return value and given default value otherwise + // If 'key' is found, return its value. Otherwise, return `default_value'. const auto it = find(std::forward(key)); if (it != end()) { @@ -2329,7 +2357,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // value only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - // if pointer resolves a value, return it or use default value + // If the pointer resolves to a value, return it. Otherwise, return + // 'default_value'. JSON_TRY { return ptr.get_checked(this).template get(); @@ -2354,7 +2383,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // value only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - // if pointer resolves a value, return it or use default value + // If the pointer resolves to a value, return it. Otherwise, return + // 'default_value'. JSON_TRY { return ptr.get_checked(this).template get(); @@ -2426,9 +2456,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec template < class IteratorType, detail::enable_if_t < std::is_same::value || std::is_same::value, int > = 0 > - IteratorType erase(IteratorType pos) + IteratorType erase(IteratorType pos) // NOLINT(performance-unnecessary-value-param) { - // make sure iterator fits the current value + // make sure the iterator fits the current value if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) { JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); @@ -2496,9 +2526,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec template < class IteratorType, detail::enable_if_t < std::is_same::value || std::is_same::value, int > = 0 > - IteratorType erase(IteratorType first, IteratorType last) + IteratorType erase(IteratorType first, IteratorType last) // NOLINT(performance-unnecessary-value-param) { - // make sure iterator fits the current value + // make sure the iterator fits the current value if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) { JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", this)); @@ -3093,7 +3123,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this)); } - // transform null object into an array + // transform a null object into an array if (is_null()) { m_data.m_type = value_t::array; @@ -3101,7 +3131,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec assert_invariant(); } - // add element to array (move semantics) + // add the element to the array (move semantics) const auto old_capacity = m_data.m_value.array->capacity(); m_data.m_value.array->push_back(std::move(val)); set_parent(m_data.m_value.array->back(), old_capacity); @@ -3126,7 +3156,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this)); } - // transform null object into an array + // transform a null object into an array if (is_null()) { m_data.m_type = value_t::array; @@ -3134,7 +3164,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec assert_invariant(); } - // add element to array + // add the element to the array const auto old_capacity = m_data.m_value.array->capacity(); m_data.m_value.array->push_back(val); set_parent(m_data.m_value.array->back(), old_capacity); @@ -3158,7 +3188,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this)); } - // transform null object into an object + // transform a null object into an object if (is_null()) { m_data.m_type = value_t::object; @@ -3166,7 +3196,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec assert_invariant(); } - // add element to object + // add the element to the object auto res = m_data.m_value.object->insert(val); set_parent(res.first->second); } @@ -3214,7 +3244,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(311, detail::concat("cannot use emplace_back() with ", type_name()), this)); } - // transform null object into an array + // transform a null object into an array if (is_null()) { m_data.m_type = value_t::array; @@ -3222,7 +3252,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec assert_invariant(); } - // add element to array (perfect forwarding) + // add the element to the array (perfect forwarding) const auto old_capacity = m_data.m_value.array->capacity(); m_data.m_value.array->emplace_back(std::forward(args)...); return set_parent(m_data.m_value.array->back(), old_capacity); @@ -3239,7 +3269,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(311, detail::concat("cannot use emplace() with ", type_name()), this)); } - // transform null object into an object + // transform a null object into an object if (is_null()) { m_data.m_type = value_t::object; @@ -3247,11 +3277,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec assert_invariant(); } - // add element to array (perfect forwarding) + // add the element to the array (perfect forwarding) auto res = m_data.m_value.object->emplace(std::forward(args)...); set_parent(res.first->second); - // create result iterator and set iterator to the result of emplace + // create a result iterator and set iterator to the result of emplace auto it = begin(); it.m_it.object_iterator = res.first; @@ -3263,7 +3293,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @note: This uses std::distance to support GCC 4.8, /// see https://github.com/nlohmann/json/pull/1257 template - iterator insert_iterator(const_iterator pos, Args&& ... args) + iterator insert_iterator(const_iterator pos, Args&& ... args) // NOLINT(performance-unnecessary-value-param) { iterator result(this); JSON_ASSERT(m_data.m_value.array != nullptr); @@ -3282,7 +3312,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief inserts element into array /// @sa https://json.nlohmann.me/api/basic_json/insert/ - iterator insert(const_iterator pos, const basic_json& val) + iterator insert(const_iterator pos, const basic_json& val) // NOLINT(performance-unnecessary-value-param) { // insert only works for arrays if (JSON_HEDLEY_LIKELY(is_array())) @@ -3302,14 +3332,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief inserts element into array /// @sa https://json.nlohmann.me/api/basic_json/insert/ - iterator insert(const_iterator pos, basic_json&& val) + iterator insert(const_iterator pos, basic_json&& val) // NOLINT(performance-unnecessary-value-param) { return insert(pos, val); } /// @brief inserts copies of element into array /// @sa https://json.nlohmann.me/api/basic_json/insert/ - iterator insert(const_iterator pos, size_type cnt, const basic_json& val) + iterator insert(const_iterator pos, size_type cnt, const basic_json& val) // NOLINT(performance-unnecessary-value-param) { // insert only works for arrays if (JSON_HEDLEY_LIKELY(is_array())) @@ -3329,7 +3359,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief inserts range of elements into array /// @sa https://json.nlohmann.me/api/basic_json/insert/ - iterator insert(const_iterator pos, const_iterator first, const_iterator last) + iterator insert(const_iterator pos, const_iterator first, const_iterator last) // NOLINT(performance-unnecessary-value-param) { // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) @@ -3360,7 +3390,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief inserts elements from initializer list into array /// @sa https://json.nlohmann.me/api/basic_json/insert/ - iterator insert(const_iterator pos, initializer_list_t ilist) + iterator insert(const_iterator pos, initializer_list_t ilist) // NOLINT(performance-unnecessary-value-param) { // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) @@ -3380,7 +3410,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief inserts range of elements into object /// @sa https://json.nlohmann.me/api/basic_json/insert/ - void insert(const_iterator first, const_iterator last) + void insert(const_iterator first, const_iterator last) // NOLINT(performance-unnecessary-value-param) { // insert only works for objects if (JSON_HEDLEY_UNLIKELY(!is_object())) @@ -3401,6 +3431,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } m_data.m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); + set_parents(); } /// @brief updates a JSON object from another object, overwriting existing keys @@ -3412,9 +3443,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief updates a JSON object from another object, overwriting existing keys /// @sa https://json.nlohmann.me/api/basic_json/update/ - void update(const_iterator first, const_iterator last, bool merge_objects = false) + void update(const_iterator first, const_iterator last, bool merge_objects = false) // NOLINT(performance-unnecessary-value-param) { - // implicitly convert null value to an empty object + // implicitly convert a null value to an empty object if (is_null()) { m_data.m_type = value_t::object; @@ -3974,7 +4005,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/ friend std::ostream& operator<<(std::ostream& o, const basic_json& j) { - // read width member and use it as indentation parameter if nonzero + // read width member and use it as the indentation parameter if nonzero const bool pretty_print = o.width() > 0; const auto indentation = pretty_print ? o.width() : 0; @@ -4013,12 +4044,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec template JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json parse(InputType&& i, - const parser_callback_t cb = nullptr, + 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(i)), cb, allow_exceptions, ignore_comments).parse(true, result); + parser(detail::input_adapter(std::forward(i)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved,accessForwarded] return result; } @@ -4028,24 +4060,26 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json parse(IteratorType first, IteratorType last, - const parser_callback_t cb = nullptr, + 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)), cb, allow_exceptions, ignore_comments).parse(true, result); + 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; } JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) static basic_json parse(detail::span_input_adapter&& i, - const parser_callback_t cb = nullptr, + 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(), cb, allow_exceptions, ignore_comments).parse(true, result); + parser(i.get(), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved] return result; } @@ -4053,26 +4087,29 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/accept/ template 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(i)), nullptr, false, ignore_comments).accept(true); + return parser(detail::input_adapter(std::forward(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 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 @@ -4082,11 +4119,28 @@ 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) { +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-pointer-compare" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull-compare" +#endif + if (sax == nullptr) + { + JSON_THROW(other_error::create(502, "SAX handler must not be null", nullptr)); + } +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif auto ia = detail::input_adapter(std::forward(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(std::move(ia), format).sax_parse(format, sax, strict); } @@ -4097,11 +4151,28 @@ 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) { +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-pointer-compare" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull-compare" +#endif + if (sax == nullptr) + { + JSON_THROW(other_error::create(502, "SAX handler must not be null", nullptr)); + } +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif 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(std::move(ia), format).sax_parse(format, sax, strict); } @@ -4116,12 +4187,29 @@ 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) { +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-pointer-compare" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull-compare" +#endif + if (sax == nullptr) + { + JSON_THROW(other_error::create(502, "SAX handler must not be null", nullptr)); + } +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif 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(std::move(ia), format).sax_parse(format, sax, strict); } @@ -4176,8 +4264,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: - default: return "number"; + default: + return "invalid"; } } @@ -4224,6 +4313,23 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec basic_json* m_parent = nullptr; #endif +#if JSON_DIAGNOSTIC_POSITIONS + /// the start position of the value + std::size_t start_position = std::string::npos; + /// the end position of the value + std::size_t end_position = std::string::npos; + public: + constexpr std::size_t start_pos() const noexcept + { + return start_position; + } + + constexpr std::size_t end_pos() const noexcept + { + return end_position; + } +#endif + ////////////////////////////////////////// // binary serialization/deserialization // ////////////////////////////////////////// @@ -4309,27 +4415,30 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/ static std::vector to_bjdata(const basic_json& j, const bool use_size = false, - const bool use_type = false) + const bool use_type = false, + const bjdata_version_t version = bjdata_version_t::draft2) { std::vector result; - to_bjdata(j, result, use_size, use_type); + to_bjdata(j, result, use_size, use_type, version); return result; } /// @brief create a BJData serialization of a given JSON value /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/ static void to_bjdata(const basic_json& j, detail::output_adapter o, - const bool use_size = false, const bool use_type = false) + const bool use_size = false, const bool use_type = false, + const bjdata_version_t version = bjdata_version_t::draft2) { - binary_writer(o).write_ubjson(j, use_size, use_type, true, true); + binary_writer(o).write_ubjson(j, use_size, use_type, true, true, version); } /// @brief create a BJData serialization of a given JSON value /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/ static void to_bjdata(const basic_json& j, detail::output_adapter o, - const bool use_size = false, const bool use_type = false) + const bool use_size = false, const bool use_type = false, + const bjdata_version_t version = bjdata_version_t::draft2) { - binary_writer(o).write_ubjson(j, use_size, use_type, true, true); + binary_writer(o).write_ubjson(j, use_size, use_type, true, true, version); } /// @brief create a BSON serialization of a given JSON value @@ -4365,9 +4474,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward(i)); - const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -4381,9 +4490,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); - const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -4406,10 +4515,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = i.get(); + detail::json_sax_dom_parser sdp(result, allow_exceptions); // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) - const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -4422,9 +4531,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward(i)); - const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -4437,9 +4546,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); - const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -4460,10 +4569,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = i.get(); + detail::json_sax_dom_parser sdp(result, allow_exceptions); // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) - const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -4476,9 +4585,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward(i)); - const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -4491,9 +4600,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); - const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -4514,10 +4623,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = i.get(); + detail::json_sax_dom_parser sdp(result, allow_exceptions); // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) - const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -4530,9 +4639,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward(i)); - const bool res = binary_reader(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -4545,9 +4654,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); - const bool res = binary_reader(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -4560,9 +4669,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward(i)); - const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -4575,9 +4684,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); - const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -4598,10 +4707,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = i.get(); + detail::json_sax_dom_parser sdp(result, allow_exceptions); // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) - const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } /// @} @@ -4702,7 +4811,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // the valid JSON Patch operations enum class patch_operations {add, remove, replace, move, copy, test, invalid}; - const auto get_op = [](const std::string & op) + const auto get_op = [](const string_t& op) { if (op == "add") { @@ -4733,7 +4842,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec }; // wrapper for "add" operation; add value at ptr - const auto operation_add = [&result](json_pointer & ptr, basic_json val) + const auto operation_add = [&result](json_pointer & ptr, const basic_json & val) { // adding to the root of the target document means replacing it if (ptr.empty()) @@ -4749,7 +4858,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec result.at(top_pointer); } - // get reference to parent of JSON pointer ptr + // get reference to the parent of the JSON pointer ptr const auto last_path = ptr.back(); ptr.pop_back(); // parent must exist when performing patch add per RFC6902 specs @@ -4787,7 +4896,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec break; } - // if there exists a parent it cannot be primitive + // if there exists a parent, it cannot be primitive case value_t::string: // LCOV_EXCL_LINE case value_t::boolean: // LCOV_EXCL_LINE case value_t::number_integer: // LCOV_EXCL_LINE @@ -4803,7 +4912,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // wrapper for "remove" operation; remove value at ptr const auto operation_remove = [this, & result](json_pointer & ptr) { - // get reference to parent of JSON pointer ptr + // get reference to the parent of the JSON pointer ptr const auto last_path = ptr.back(); ptr.pop_back(); basic_json& parent = result.at(ptr); @@ -4839,8 +4948,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec for (const auto& val : json_patch) { // wrapper to get a value for an operation - const auto get_value = [&val](const std::string & op, - const std::string & member, + const auto get_value = [&val](const string_t& op, + const string_t& member, bool string_type) -> basic_json & { // find value @@ -4849,14 +4958,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // context-sensitive error message const auto error_msg = (op == "op") ? "operation" : detail::concat("operation '", op, '\''); // NOLINT(bugprone-unused-local-non-trivial-variable) - // check if desired value is present + // check if the desired value is present if (JSON_HEDLEY_UNLIKELY(it == val.m_data.m_value.object->end())) { // NOLINTNEXTLINE(performance-inefficient-string-concatenation) JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have member '", member, "'"), &val)); } - // check if result is of type string + // check if the result is of type string if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) { // NOLINTNEXTLINE(performance-inefficient-string-concatenation) @@ -4874,8 +4983,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } // collect mandatory members - const auto op = get_value("op", "op", true).template get(); - const auto path = get_value(op, "path", true).template get(); + const auto op = get_value("op", "op", true).template get(); + const auto path = get_value(op, "path", true).template get(); json_pointer ptr(path); switch (get_op(op)) @@ -4901,7 +5010,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case patch_operations::move: { - const auto from_path = get_value("move", "from", true).template get(); + const auto from_path = get_value("move", "from", true).template get(); json_pointer from_ptr(from_path); // the "from" location must exist - use at() @@ -4918,7 +5027,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case patch_operations::copy: { - const auto from_path = get_value("copy", "from", true).template get(); + const auto from_path = get_value("copy", "from", true).template get(); const json_pointer from_ptr(from_path); // the "from" location must exist - use at() @@ -4945,7 +5054,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // ignore out of range errors: success remains false } - // throw an exception if test fails + // throw an exception if the test fails if (JSON_HEDLEY_UNLIKELY(!success)) { JSON_THROW(other_error::create(501, detail::concat("unsuccessful: ", val.dump()), &val)); @@ -4978,12 +5087,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/diff/ JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json diff(const basic_json& source, const basic_json& target, - const std::string& path = "") + const string_t& path = "") { // the patch basic_json result(value_t::array); - // if the values are the same, return empty patch + // if the values are the same, return an empty patch if (source == target) { return result; @@ -5008,7 +5117,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec while (i < source.size() && i < target.size()) { // recursive call to compare array values at index i - auto temp_diff = diff(source[i], target[i], detail::concat(path, '/', std::to_string(i))); + auto temp_diff = diff(source[i], target[i], detail::concat(path, '/', detail::to_string(i))); result.insert(result.end(), temp_diff.begin(), temp_diff.end()); ++i; } @@ -5025,7 +5134,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec result.insert(result.begin() + end_index, object( { {"op", "remove"}, - {"path", detail::concat(path, '/', std::to_string(i))} + {"path", detail::concat(path, '/', detail::to_string(i))} })); ++i; } @@ -5036,7 +5145,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec result.push_back( { {"op", "add"}, - {"path", detail::concat(path, "/-")}, + {"path", detail::concat(path, "/-")}, {"value", target[i]} }); ++i; @@ -5051,7 +5160,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec for (auto it = source.cbegin(); it != source.cend(); ++it) { // escape the key name to be used in a JSON patch - const auto path_key = detail::concat(path, '/', detail::escape(it.key())); + const auto path_key = detail::concat(path, '/', detail::escape(it.key())); if (target.find(it.key()) != target.end()) { @@ -5075,7 +5184,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (source.find(it.key()) == source.end()) { // found a key that is not in this -> add it - const auto path_key = detail::concat(path, '/', detail::escape(it.key())); + const auto path_key = detail::concat(path, '/', detail::escape(it.key())); result.push_back( { {"op", "add"}, {"path", path_key}, @@ -5097,7 +5206,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case value_t::discarded: default: { - // both primitive type: replace value + // both primitive types: replace value result.push_back( { {"op", "replace"}, {"path", path}, {"value", target} @@ -5165,26 +5274,44 @@ inline namespace json_literals /// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/ JSON_HEDLEY_NON_NULL(1) #if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) - inline nlohmann::json operator ""_json(const char* s, std::size_t n) + inline nlohmann::json operator""_json(const char* s, std::size_t n) #else - inline nlohmann::json operator "" _json(const char* s, std::size_t n) + // GCC 4.8 requires a space between "" and suffix + inline nlohmann::json operator"" _json(const char* s, std::size_t n) #endif { return nlohmann::json::parse(s, s + n); } +#if defined(__cpp_char8_t) +JSON_HEDLEY_NON_NULL(1) +inline nlohmann::json operator""_json(const char8_t* s, std::size_t n) +{ + return nlohmann::json::parse(reinterpret_cast(s), + reinterpret_cast(s) + n); +} +#endif + /// @brief user-defined string literal for JSON pointer /// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/ JSON_HEDLEY_NON_NULL(1) #if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) - inline nlohmann::json::json_pointer operator ""_json_pointer(const char* s, std::size_t n) + inline nlohmann::json::json_pointer operator""_json_pointer(const char* s, std::size_t n) #else - inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) + // GCC 4.8 requires a space between "" and suffix + inline nlohmann::json::json_pointer operator"" _json_pointer(const char* s, std::size_t n) #endif { return nlohmann::json::json_pointer(std::string(s, n)); } +#if defined(__cpp_char8_t) +inline nlohmann::json::json_pointer operator""_json_pointer(const char8_t* s, std::size_t n) +{ + return nlohmann::json::json_pointer(std::string(reinterpret_cast(s), n)); +} +#endif + } // namespace json_literals } // namespace literals NLOHMANN_JSON_NAMESPACE_END @@ -5245,11 +5372,12 @@ inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC #if JSON_USE_GLOBAL_UDLS #if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) - using nlohmann::literals::json_literals::operator ""_json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers) - using nlohmann::literals::json_literals::operator ""_json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers) + using nlohmann::literals::json_literals::operator""_json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers) + using nlohmann::literals::json_literals::operator""_json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers) #else - using nlohmann::literals::json_literals::operator "" _json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers) - using nlohmann::literals::json_literals::operator "" _json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers) + // GCC 4.8 requires a space between "" and suffix + using nlohmann::literals::json_literals::operator"" _json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers) + using nlohmann::literals::json_literals::operator"" _json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers) #endif #endif diff --git a/include/nlohmann/json_fwd.hpp b/include/nlohmann/json_fwd.hpp index 32bde590f8..be05ebeef1 100644 --- a/include/nlohmann/json_fwd.hpp +++ b/include/nlohmann/json_fwd.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ diff --git a/include/nlohmann/ordered_map.hpp b/include/nlohmann/ordered_map.hpp index 39e4a50a14..d8606f82fd 100644 --- a/include/nlohmann/ordered_map.hpp +++ b/include/nlohmann/ordered_map.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once @@ -26,7 +26,7 @@ NLOHMANN_JSON_NAMESPACE_BEGIN /// for use within nlohmann::basic_json template , class Allocator = std::allocator>> - struct ordered_map : std::vector, Allocator> + struct ordered_map : std::vector, Allocator> { using key_type = Key; using mapped_type = T; @@ -226,7 +226,7 @@ template , // Since we cannot move const Keys, we re-construct them in place. // We start at first and re-construct (viz. copy) the elements from - // the back of the vector. Example for first iteration: + // the back of the vector. Example for the first iteration: // ,--------. // v | destroy e and re-construct with h @@ -341,7 +341,7 @@ template , template using require_input_iter = typename std::enable_if::iterator_category, - std::input_iterator_tag>::value>::type; + std::input_iterator_tag>::value>::type; template> void insert(InputIt first, InputIt last) diff --git a/include/nlohmann/thirdparty/hedley/hedley.hpp b/include/nlohmann/thirdparty/hedley/hedley.hpp index a1dc64f600..437989c0aa 100644 --- a/include/nlohmann/thirdparty/hedley/hedley.hpp +++ b/include/nlohmann/thirdparty/hedley/hedley.hpp @@ -2,15 +2,16 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-FileCopyrightText: 2016-2021 Evan Nemerson // SPDX-License-Identifier: MIT /* Hedley - https://nemequ.github.io/hedley * Created by Evan Nemerson + * SPDX-License-Identifier: CC0-1.0 */ #if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) diff --git a/include/nlohmann/thirdparty/hedley/hedley_undef.hpp b/include/nlohmann/thirdparty/hedley/hedley_undef.hpp index c0aee2bb30..1b8bd43384 100644 --- a/include/nlohmann/thirdparty/hedley/hedley_undef.hpp +++ b/include/nlohmann/thirdparty/hedley/hedley_undef.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once diff --git a/meson.build b/meson.build index 7a9c5ec851..a2d6e31a25 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('nlohmann_json', 'cpp', - version : '3.11.3', + version : '3.12.0', license : 'MIT', ) diff --git a/nlohmann_json.natvis b/nlohmann_json.natvis index fbe4221f2e..09a46d67d8 100644 --- a/nlohmann_json.natvis +++ b/nlohmann_json.natvis @@ -65,22 +65,22 @@ - - - null - {*(m_data.m_value.object)} - {*(m_data.m_value.array)} - {*(m_data.m_value.string)} - {m_data.m_value.boolean} - {m_data.m_value.number_integer} - {m_data.m_value.number_unsigned} - {m_data.m_value.number_float} - discarded + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded - + *(m_data.m_value.object),view(simple) - + *(m_data.m_value.array),view(simple) @@ -88,7 +88,7 @@ - + {second} second @@ -125,22 +125,22 @@ - - - null - {*(m_data.m_value.object)} - {*(m_data.m_value.array)} - {*(m_data.m_value.string)} - {m_data.m_value.boolean} - {m_data.m_value.number_integer} - {m_data.m_value.number_unsigned} - {m_data.m_value.number_float} - discarded + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded - + *(m_data.m_value.object),view(simple) - + *(m_data.m_value.array),view(simple) @@ -148,7 +148,7 @@ - + {second} second @@ -185,22 +185,22 @@ - - - null - {*(m_data.m_value.object)} - {*(m_data.m_value.array)} - {*(m_data.m_value.string)} - {m_data.m_value.boolean} - {m_data.m_value.number_integer} - {m_data.m_value.number_unsigned} - {m_data.m_value.number_float} - discarded + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded - + *(m_data.m_value.object),view(simple) - + *(m_data.m_value.array),view(simple) @@ -208,7 +208,7 @@ - + {second} second @@ -245,22 +245,22 @@ - - - null - {*(m_data.m_value.object)} - {*(m_data.m_value.array)} - {*(m_data.m_value.string)} - {m_data.m_value.boolean} - {m_data.m_value.number_integer} - {m_data.m_value.number_unsigned} - {m_data.m_value.number_float} - discarded + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded - + *(m_data.m_value.object),view(simple) - + *(m_data.m_value.array),view(simple) @@ -268,7 +268,7 @@ - + {second} second diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index a858728c4c..3f154746c7 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT /****************************************************************************\ @@ -34,10 +34,10 @@ // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -47,10 +47,10 @@ // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -59,20 +59,24 @@ #ifndef JSON_SKIP_LIBRARY_VERSION_CHECK #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) - #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 3 + #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 12 || NLOHMANN_JSON_VERSION_PATCH != 0 #warning "Already included a different version of the library!" #endif #endif #endif #define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) -#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum) -#define NLOHMANN_JSON_VERSION_PATCH 3 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_MINOR 12 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_PATCH 0 // NOLINT(modernize-macro-to-enum) #ifndef JSON_DIAGNOSTICS #define JSON_DIAGNOSTICS 0 #endif +#ifndef JSON_DIAGNOSTIC_POSITIONS + #define JSON_DIAGNOSTIC_POSITIONS 0 +#endif + #ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 #endif @@ -83,6 +87,12 @@ #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS #endif +#if JSON_DIAGNOSTIC_POSITIONS + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS _dp +#else + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS +#endif + #if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp #else @@ -94,14 +104,15 @@ #endif // Construct the namespace ABI tags component -#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b -#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \ - NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) +#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) json_abi ## a ## b ## c +#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c) \ + NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) #define NLOHMANN_JSON_ABI_TAGS \ NLOHMANN_JSON_ABI_TAGS_CONCAT( \ NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ - NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON) + NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \ + NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS) // Construct the namespace version component #define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ @@ -149,10 +160,10 @@ // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -172,10 +183,10 @@ // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -192,10 +203,10 @@ // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -208,10 +219,10 @@ // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -220,10 +231,10 @@ // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -233,10 +244,10 @@ // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -320,15 +331,16 @@ NLOHMANN_JSON_NAMESPACE_END // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-FileCopyrightText: 2016-2021 Evan Nemerson // SPDX-License-Identifier: MIT /* Hedley - https://nemequ.github.io/hedley * Created by Evan Nemerson + * SPDX-License-Identifier: CC0-1.0 */ #if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) @@ -2383,16 +2395,27 @@ JSON_HEDLEY_DIAGNOSTIC_POP #endif // C++ language standard detection -// if the user manually specified the used c++ version this is skipped -#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) - #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) +// if the user manually specified the used C++ version, this is skipped +#if !defined(JSON_HAS_CPP_26) && !defined(JSON_HAS_CPP_23) && !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) + #if (defined(__cplusplus) && __cplusplus > 202302L) || (defined(_MSVC_LANG) && _MSVC_LANG > 202302L) + #define JSON_HAS_CPP_26 + #define JSON_HAS_CPP_23 + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus > 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG > 202002L) + #define JSON_HAS_CPP_23 #define JSON_HAS_CPP_20 #define JSON_HAS_CPP_17 #define JSON_HAS_CPP_14 - #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #elif (defined(__cplusplus) && __cplusplus > 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG > 201703L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus > 201402L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 #define JSON_HAS_CPP_17 #define JSON_HAS_CPP_14 - #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #elif (defined(__cplusplus) && __cplusplus > 201103L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) #define JSON_HAS_CPP_14 #endif // the cpp 11 flag is always specified because it is the minimal required version @@ -2475,7 +2498,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP #endif #ifndef JSON_HAS_RANGES - // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error + // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has a syntax error #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427 #define JSON_HAS_RANGES 0 #elif defined(__cpp_lib_ranges) @@ -2552,7 +2575,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define JSON_ASSERT(x) assert(x) #endif -// allow to access some private functions (needed by the test suite) +// allow accessing some private functions (needed by the test suite) #if defined(JSON_TESTS_PRIVATE) #define JSON_PRIVATE_UNLESS_TESTED public #else @@ -2568,7 +2591,9 @@ JSON_HEDLEY_DIAGNOSTIC_POP template \ inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ { \ + /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \ static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + /* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on */ \ static const std::pair m[] = __VA_ARGS__; \ auto it = std::find_if(std::begin(m), std::end(m), \ [e](const std::pair& ej_pair) -> bool \ @@ -2580,7 +2605,9 @@ JSON_HEDLEY_DIAGNOSTIC_POP template \ inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ { \ + /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \ static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + /* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on */ \ static const std::pair m[] = __VA_ARGS__; \ auto it = std::find_if(std::begin(m), std::end(m), \ [&j](const std::pair& ej_pair) -> bool \ @@ -2743,42 +2770,146 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; #define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); -#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); +#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = !nlohmann_json_j.is_null() ? nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1) : nlohmann_json_default_obj.v1; /*! @brief macro @def NLOHMANN_DEFINE_TYPE_INTRUSIVE @since version 3.9.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/ */ #define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ - friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + template::value, int> = 0> \ + friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT +@since version 3.11.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/ +*/ #define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ - friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + template::value, int> = 0> \ + friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE +@since version 3.11.3 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/ +*/ #define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ - friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } + template::value, int> = 0> \ + friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } /*! @brief macro @def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE @since version 3.9.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/ */ #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ - inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + template::value, int> = 0> \ + void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT +@since version 3.11.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/ +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + template::value, int> = 0> \ + void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE +@since version 3.11.3 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/ +*/ #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ - inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } + template::value, int> = 0> \ + void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } -#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ - inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } +/*! +@brief macro +@def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE +@since version 3.12.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ +*/ +#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(Type, BaseType, ...) \ + template::value, int> = 0> \ + friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT +@since version 3.12.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ +*/ +#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \ + template::value, int> = 0> \ + friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE +@since version 3.12.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ +*/ +#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \ + template::value, int> = 0> \ + friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE +@since version 3.12.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ +*/ +#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(Type, BaseType, ...) \ + template::value, int> = 0> \ + void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT +@since version 3.12.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ +*/ +#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \ + template::value, int> = 0> \ + void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE +@since version 3.12.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ +*/ +#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \ + template::value, int> = 0> \ + void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } // inspired from https://stackoverflow.com/a/26745591 -// allows to call any std function as if (e.g. with begin): +// allows calling any std function as if (e.g., with begin): // using std::begin; begin(x); // // it allows using the detected idiom to retrieve the return type @@ -2939,10 +3070,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -2972,10 +3103,10 @@ inline void replace_substring(StringType& s, const StringType& f, const StringType& t) { JSON_ASSERT(!f.empty()); - for (auto pos = s.find(f); // find first occurrence of f + for (auto pos = s.find(f); // find the first occurrence of f pos != StringType::npos; // make sure f was found s.replace(pos, f.size(), t), // replace with t, and - pos = s.find(f, pos + t.size())) // find next occurrence of f + pos = s.find(f, pos + t.size())) // find the next occurrence of f {} } @@ -3002,7 +3133,7 @@ inline StringType escape(StringType s) * Note the order of escaping "~1" to "/" and "~0" to "~" is important. */ template -static void unescape(StringType& s) +inline void unescape(StringType& s) { replace_substring(s, StringType{"~1"}, StringType{"/"}); replace_substring(s, StringType{"~0"}, StringType{"~"}); @@ -3014,10 +3145,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -3056,10 +3187,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-FileCopyrightText: 2018 The Abseil Authors // SPDX-License-Identifier: MIT @@ -3219,7 +3350,7 @@ struct static_const #endif template -inline constexpr std::array make_array(Args&& ... args) +constexpr std::array make_array(Args&& ... args) { return std::array {{static_cast(std::forward(args))...}}; } @@ -3230,27 +3361,29 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include // numeric_limits +#include // char_traits +#include // tuple #include // false_type, is_constructible, is_integral, is_same, true_type #include // declval -#include // tuple -#include // char_traits - +#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L + #include // byte +#endif // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -3293,7 +3426,7 @@ struct iterator_traits template struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> - : iterator_types + : iterator_types { }; @@ -3315,10 +3448,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -3335,10 +3468,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -3359,10 +3492,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ @@ -3453,12 +3586,12 @@ namespace detail // Note to maintainers: // -// Every trait in this file expects a non CV-qualified type. +// Every trait in this file expects a non-CV-qualified type. // The only exceptions are in the 'aliases for detected' section -// (i.e. those of the form: decltype(T::member_function(std::declval()))) +// (i.e., those of the form: decltype(T::member_function(std::declval()))) // // In this case, T has to be properly CV-qualified to constraint the function arguments -// (e.g. to_json(BasicJsonType&, const T&)) +// (e.g., to_json(BasicJsonType&, const T&)) template struct is_basic_json : std::false_type {}; @@ -3466,7 +3599,7 @@ NLOHMANN_BASIC_JSON_TPL_DECLARATION struct is_basic_json : std::true_type {}; // used by exceptions create() member functions -// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t +// true_type for the pointer to possibly cv-qualified basic_json or std::nullptr_t // false_type otherwise template struct is_basic_json_context : @@ -3624,7 +3757,7 @@ struct char_traits : std::char_traits static constexpr int_type eof() noexcept { - return static_cast(EOF); + return static_cast(std::char_traits::eof()); } }; @@ -3648,9 +3781,33 @@ struct char_traits : std::char_traits static constexpr int_type eof() noexcept { - return static_cast(EOF); + return static_cast(std::char_traits::eof()); + } +}; + +#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L +template<> +struct char_traits : std::char_traits +{ + using char_type = std::byte; + using int_type = uint64_t; + + static int_type to_int_type(char_type c) noexcept + { + return static_cast(std::to_integer(c)); + } + + static char_type to_char_type(int_type i) noexcept + { + return std::byte(static_cast(i)); + } + + static constexpr int_type eof() noexcept + { + return static_cast(std::char_traits::eof()); } }; +#endif /////////////////// // is_ functions // @@ -3668,25 +3825,25 @@ template struct negation : std::integral_constant < bool, !B::value > { // Reimplementation of is_constructible and is_default_constructible, due to them being broken for // std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). -// This causes compile errors in e.g. clang 3.5 or gcc 4.9. +// This causes compile errors in e.g., Clang 3.5 or GCC 4.9. template struct is_default_constructible : std::is_default_constructible {}; template struct is_default_constructible> - : conjunction, is_default_constructible> {}; + : conjunction, is_default_constructible> {}; template struct is_default_constructible> - : conjunction, is_default_constructible> {}; + : conjunction, is_default_constructible> {}; template struct is_default_constructible> - : conjunction...> {}; + : conjunction...> {}; template struct is_default_constructible> - : conjunction...> {}; + : conjunction...> {}; template struct is_constructible : std::is_constructible {}; @@ -3748,7 +3905,7 @@ using range_value_t = value_type_t>>; // The following implementation of is_complete_type is taken from // https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ -// and is written by Xiang Fan who agreed to using it in this library. +// and is written by Xiang Fan who agreed to use it in this library. template struct is_complete_type : std::false_type {}; @@ -3884,8 +4041,8 @@ is_detected::value&& // special case for types like std::filesystem::path whose iterator's value_type are themselves // c.f. https://github.com/nlohmann/json/pull/3073 !std::is_same>::value&& - is_complete_type < - detected_t>::value >> +is_complete_type < +detected_t>::value >> { using value_type = range_value_t; @@ -3972,20 +4129,35 @@ struct is_specialization_of> : std::true_type {}; template using is_json_pointer = is_specialization_of<::nlohmann::json_pointer, uncvref_t>; +// checks if B is a json_pointer +template +struct is_json_pointer_of : std::false_type {}; + +template +struct is_json_pointer_of> : std::true_type {}; + +template +struct is_json_pointer_of&> : std::true_type {}; + // checks if A and B are comparable using Compare functor template struct is_comparable : std::false_type {}; +// We exclude json_pointer here, because the checks using Compare(A, B) will +// use json_pointer::operator string_t() which triggers a deprecation warning +// for GCC. See https://github.com/nlohmann/json/issues/4621. The call to +// is_json_pointer_of can be removed once the deprecated function has been +// removed. template -struct is_comparable()(std::declval(), std::declval())), -decltype(std::declval()(std::declval(), std::declval())) +struct is_comparable < Compare, A, B, enable_if_t < !is_json_pointer_of::value +&& std::is_constructible ()(std::declval(), std::declval()))>::value +&& std::is_constructible ()(std::declval(), std::declval()))>::value >> : std::true_type {}; template using detect_is_transparent = typename T::is_transparent; -// type trait to check if KeyType can be used as object key (without a BasicJsonType) +// type trait to check if KeyType can be used as an object key (without a BasicJsonType) // see is_usable_as_basic_json_key_type below template> @@ -3999,7 +4171,7 @@ using is_usable_as_key_type = typename std::conditional < std::true_type, std::false_type >::type; -// type trait to check if KeyType can be used as object key +// type trait to check if KeyType can be used as an object key // true if: // - KeyType is comparable with BasicJsonType::object_t::key_type // - if ExcludeObjectKeyType is true, KeyType is not BasicJsonType::object_t::key_type @@ -4008,12 +4180,12 @@ using is_usable_as_key_type = typename std::conditional < template> using is_usable_as_basic_json_key_type = typename std::conditional < - is_usable_as_key_type::value - && !is_json_iterator_of::value, - std::true_type, - std::false_type >::type; + is_usable_as_key_type::value + && !is_json_iterator_of::value, + std::true_type, + std::false_type >::type; template using detect_erase_with_key_type = decltype(std::declval().erase(std::declval())); @@ -4042,7 +4214,7 @@ struct is_ordered_map template static one test( decltype(&C::capacity) ) ; template static two test(...); - enum { value = sizeof(test(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + enum { value = sizeof(test(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg,cppcoreguidelines-use-enum-class) }; // to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324) @@ -4147,7 +4319,7 @@ struct value_in_range_of_impl1 }; template -inline constexpr bool value_in_range_of(T val) +constexpr bool value_in_range_of(T val) { return value_in_range_of_impl1::test(val); } @@ -4163,7 +4335,7 @@ namespace impl { template -inline constexpr bool is_c_string() +constexpr bool is_c_string() { using TUnExt = typename std::remove_extent::type; using TUnCVExt = typename std::remove_cv::type; @@ -4191,7 +4363,7 @@ namespace impl { template -inline constexpr bool is_transparent() +constexpr bool is_transparent() { return is_detected::value; } @@ -4210,10 +4382,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -4358,6 +4530,18 @@ inline OutStringType concat(Args && ... args) NLOHMANN_JSON_NAMESPACE_END +// With -Wweak-vtables, Clang will complain about the exception classes as they +// have no out-of-line virtual method definitions and their vtable will be +// emitted in every translation unit. This issue cannot be fixed with a +// header-only library as there is no implementation file to move these +// functions to. As a result, we suppress this warning here to avoid client +// code stumbling over this. See https://github.com/nlohmann/json/issues/4087 +// for a discussion. +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wweak-vtables" +#endif + NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { @@ -4452,16 +4636,34 @@ class exception : public std::exception { return concat(a, '/', detail::escape(b)); }); - return concat('(', str, ") "); + + return concat('(', str, ") ", get_byte_positions(leaf_element)); #else - static_cast(leaf_element); - return ""; + return get_byte_positions(leaf_element); #endif } private: /// an exception object as storage for error messages std::runtime_error m; +#if JSON_DIAGNOSTIC_POSITIONS + template + static std::string get_byte_positions(const BasicJsonType* leaf_element) + { + if ((leaf_element->start_pos() != std::string::npos) && (leaf_element->end_pos() != std::string::npos)) + { + return concat("(bytes ", std::to_string(leaf_element->start_pos()), "-", std::to_string(leaf_element->end_pos()), ") "); + } + return ""; + } +#else + template + static std::string get_byte_positions(const BasicJsonType* leaf_element) + { + static_cast(leaf_element); + return ""; + } +#endif }; /// @brief exception indicating a parse error @@ -4589,6 +4791,10 @@ class other_error : public exception } // namespace detail NLOHMANN_JSON_NAMESPACE_END +#if defined(__clang__) + #pragma clang diagnostic pop +#endif + // #include // #include @@ -4596,10 +4802,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -4620,10 +4826,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -4640,7 +4846,7 @@ namespace std_fs = std::experimental::filesystem; } // namespace detail NLOHMANN_JSON_NAMESPACE_END #elif JSON_HAS_FILESYSTEM -#include +#include // NOLINT(build/c++17) NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { @@ -4656,6 +4862,15 @@ NLOHMANN_JSON_NAMESPACE_END // #include +// include after macro_scope.hpp +#ifdef JSON_HAS_CPP_17 + #include // optional +#endif + +#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM + #include // u8string_view +#endif + NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { @@ -4670,6 +4885,21 @@ inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n) n = nullptr; } +#ifdef JSON_HAS_CPP_17 +template +void from_json(const BasicJsonType& j, std::optional& opt) +{ + if (j.is_null()) + { + opt = std::nullopt; + } + else + { + opt.emplace(j.template get()); + } +} +#endif // JSON_HAS_CPP_17 + // overloads for basic_json template parameters template < typename BasicJsonType, typename ArithmeticType, enable_if_t < std::is_arithmetic::value&& @@ -4817,6 +5047,54 @@ auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines } } +template +auto from_json(const BasicJsonType& j, T (&arr)[N1][N2]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +-> decltype(j.template get(), void()) +{ + for (std::size_t i1 = 0; i1 < N1; ++i1) + { + for (std::size_t i2 = 0; i2 < N2; ++i2) + { + arr[i1][i2] = j.at(i1).at(i2).template get(); + } + } +} + +template +auto from_json(const BasicJsonType& j, T (&arr)[N1][N2][N3]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +-> decltype(j.template get(), void()) +{ + for (std::size_t i1 = 0; i1 < N1; ++i1) + { + for (std::size_t i2 = 0; i2 < N2; ++i2) + { + for (std::size_t i3 = 0; i3 < N3; ++i3) + { + arr[i1][i2][i3] = j.at(i1).at(i2).at(i3).template get(); + } + } + } +} + +template +auto from_json(const BasicJsonType& j, T (&arr)[N1][N2][N3][N4]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +-> decltype(j.template get(), void()) +{ + for (std::size_t i1 = 0; i1 < N1; ++i1) + { + for (std::size_t i2 = 0; i2 < N2; ++i2) + { + for (std::size_t i3 = 0; i3 < N3; ++i3) + { + for (std::size_t i4 = 0; i4 < N4; ++i4) + { + arr[i1][i2][i3][i4] = j.at(i1).at(i2).at(i3).at(i4).template get(); + } + } + } + } +} + template inline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/) { @@ -4902,7 +5180,7 @@ void()) template < typename BasicJsonType, typename T, std::size_t... Idx > std::array from_json_inplace_array_impl(BasicJsonType&& j, - identity_tag> /*unused*/, index_sequence /*unused*/) + identity_tag> /*unused*/, index_sequence /*unused*/) { return { { std::forward(j).at(Idx).template get()... } }; } @@ -4953,7 +5231,7 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) } // overload for arithmetic types, not chosen for basic_json template arguments -// (BooleanType, etc..); note: Is it really necessary to provide explicit +// (BooleanType, etc.); note: Is it really necessary to provide explicit // overloads for boolean_t etc. in case of a custom BooleanType which is not // an arithmetic type? template < typename BasicJsonType, typename ArithmeticType, @@ -5006,6 +5284,12 @@ std::tuple from_json_tuple_impl_base(BasicJsonType&& j, index_sequence< return std::make_tuple(std::forward(j).at(Idx).template get()...); } +template +std::tuple<> from_json_tuple_impl_base(BasicJsonType& /*unused*/, index_sequence<> /*unused*/) +{ + return {}; +} + template < typename BasicJsonType, class A1, class A2 > std::pair from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<0> /*unused*/) { @@ -5091,7 +5375,15 @@ inline void from_json(const BasicJsonType& j, std_fs::path& p) { JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j)); } - p = *j.template get_ptr(); + const auto& s = *j.template get_ptr(); + // Checking for C++20 standard or later can be insufficient in case the + // library support for char8_t is either incomplete or was disabled + // altogether. Use the __cpp_lib_char8_t feature test instead. +#if defined(__cpp_lib_char8_t) && (__cpp_lib_char8_t >= 201907L) + p = std_fs::path(std::u8string_view(reinterpret_cast(s.data()), s.size())); +#else + p = std_fs::u8path(s); // accepts UTF-8 encoded std::string in C++17, deprecated in C++20 +#endif } #endif @@ -5126,17 +5418,24 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT +// #include +// JSON_HAS_CPP_17 +#ifdef JSON_HAS_CPP_17 + #include // optional +#endif + #include // copy #include // begin, end -#include // string +#include // allocator_traits +#include // basic_string, char_traits #include // tuple, get #include // is_same, is_constructible, is_floating_point, is_enum, underlying_type #include // move, forward, declval, pair @@ -5146,17 +5445,16 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include // size_t -#include // input_iterator_tag -#include // string, to_string +#include // forward_iterator_tag #include // tuple_size, get, tuple_element #include // move @@ -5168,20 +5466,53 @@ NLOHMANN_JSON_NAMESPACE_END // #include -// #include +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // size_t +#include // string, to_string + +// #include NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { -template -void int_to_string( string_type& target, std::size_t value ) +template +void int_to_string(StringType& target, std::size_t value) { // For ADL using std::to_string; target = to_string(value); } + +template +StringType to_string(std::size_t value) +{ + StringType result; + int_to_string(result, value); + return result; +} + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + template class iteration_proxy_value { public: @@ -5189,7 +5520,7 @@ template class iteration_proxy_value using value_type = iteration_proxy_value; using pointer = value_type *; using reference = value_type &; - using iterator_category = std::input_iterator_tag; + using iterator_category = std::forward_iterator_tag; using string_type = typename std::remove_cv< typename std::remove_reference().key() ) >::type >::type; private: @@ -5369,7 +5700,7 @@ namespace std #endif template class tuple_size<::nlohmann::detail::iteration_proxy_value> // NOLINT(cert-dcl58-cpp) - : public std::integral_constant {}; + : public std::integral_constant {}; template class tuple_element> // NOLINT(cert-dcl58-cpp) @@ -5390,8 +5721,6 @@ class tuple_element> inline constexpr bool ::std::ranges::enable_borrowed_range<::nlohmann::detail::iteration_proxy> = true; #endif -// #include - // #include // #include @@ -5637,6 +5966,22 @@ struct external_constructor // to_json // ///////////// +#ifdef JSON_HAS_CPP_17 +template::value, int> = 0> +void to_json(BasicJsonType& j, const std::optional& opt) noexcept +{ + if (opt.has_value()) + { + j = *opt; + } + else + { + j = nullptr; + } +} +#endif + template::value, int> = 0> inline void to_json(BasicJsonType& j, T b) noexcept @@ -5783,6 +6128,13 @@ inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence< j = { std::get(t)... }; } +template +inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& /*unused*/, index_sequence<> /*unused*/) +{ + using array_t = typename BasicJsonType::array_t; + j = array_t(); +} + template::value, int > = 0> inline void to_json(BasicJsonType& j, const T& t) { @@ -5790,10 +6142,21 @@ inline void to_json(BasicJsonType& j, const T& t) } #if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM +#if defined(__cpp_lib_char8_t) +template +inline void to_json(BasicJsonType& j, const std::basic_string& s) +{ + using OtherAllocator = typename std::allocator_traits::template rebind_alloc; + j = std::basic_string, OtherAllocator>(s.begin(), s.end(), s.get_allocator()); +} +#endif + template inline void to_json(BasicJsonType& j, const std_fs::path& p) { - j = p.string(); + // Returns either a std::string or a std::u8string depending whether library + // support for char8_t is enabled. + j = p.u8string(); } #endif @@ -5868,10 +6231,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -5980,10 +6343,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -6113,10 +6476,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -6133,16 +6496,19 @@ NLOHMANN_JSON_NAMESPACE_END #include // char_traits, string #include // make_pair, move #include // vector +#ifdef __cpp_lib_byteswap + #include //byteswap +#endif // #include // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -6153,6 +6519,7 @@ NLOHMANN_JSON_NAMESPACE_END #include // begin, end, iterator_traits, random_access_iterator_tag, distance, next #include // shared_ptr, make_shared, addressof #include // accumulate +#include // streambuf #include // string, char_traits #include // enable_if, is_base_of, is_pointer, is_integral, remove_pointer #include // pair, declval @@ -6162,6 +6529,8 @@ NLOHMANN_JSON_NAMESPACE_END #include // istream #endif // JSON_NO_IO +// #include + // #include // #include @@ -6209,6 +6578,13 @@ class file_input_adapter return std::fgetc(m_file); } + // returns the number of characters successfully read + template + std::size_t get_elements(T* dest, std::size_t count = 1) + { + return fread(dest, 1, sizeof(T) * count, m_file); + } + private: /// the file pointer to read from std::FILE* m_file; @@ -6242,7 +6618,7 @@ class input_stream_adapter : is(&i), sb(i.rdbuf()) {} - // delete because of pointer members + // deleted because of pointer members input_stream_adapter(const input_stream_adapter&) = delete; input_stream_adapter& operator=(input_stream_adapter&) = delete; input_stream_adapter& operator=(input_stream_adapter&&) = delete; @@ -6256,7 +6632,7 @@ class input_stream_adapter // std::istream/std::streambuf use std::char_traits::to_int_type, to // ensure that std::char_traits::eof() and the character 0xFF do not - // end up as the same value, e.g. 0xFFFFFFFF. + // end up as the same value, e.g., 0xFFFFFFFF. std::char_traits::int_type get_character() { auto res = sb->sbumpc(); @@ -6268,6 +6644,17 @@ class input_stream_adapter return res; } + template + std::size_t get_elements(T* dest, std::size_t count = 1) + { + auto res = static_cast(sb->sgetn(reinterpret_cast(dest), static_cast(count * sizeof(T)))); + if (JSON_HEDLEY_UNLIKELY(res < count * sizeof(T))) + { + is->clear(is->rdstate() | std::ios::eofbit); + } + return res; + } + private: /// the associated input stream std::istream* is = nullptr; @@ -6299,6 +6686,26 @@ class iterator_input_adapter return char_traits::eof(); } + // for general iterators, we cannot really do something better than falling back to processing the range one-by-one + template + std::size_t get_elements(T* dest, std::size_t count = 1) + { + auto* ptr = reinterpret_cast(dest); + for (std::size_t read_index = 0; read_index < count * sizeof(T); ++read_index) + { + if (JSON_HEDLEY_LIKELY(current != end)) + { + ptr[read_index] = static_cast(*current); + std::advance(current, 1); + } + else + { + return read_index; + } + } + return count * sizeof(T); + } + private: IteratorType current; IteratorType end; @@ -6447,7 +6854,7 @@ class wide_string_input_adapter typename std::char_traits::int_type get_character() noexcept { - // check if buffer needs to be filled + // check if the buffer needs to be filled if (utf8_bytes_index == utf8_bytes_filled) { fill_buffer(); @@ -6462,6 +6869,13 @@ class wide_string_input_adapter return utf8_bytes[utf8_bytes_index++]; } + // parsing binary with wchar doesn't make sense, but since the parsing mode can be runtime, we need something here + template + std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1) + { + JSON_THROW(parse_error::create(112, 1, "wide string type cannot be interpreted as binary data", nullptr)); + } + private: BaseInputAdapter base_adapter; @@ -6497,7 +6911,7 @@ template struct is_iterator_of_multibyte { using value_type = typename std::iterator_traits::value_type; - enum + enum // NOLINT(cppcoreguidelines-use-enum-class) { value = sizeof(value_type) > 1 }; @@ -6558,10 +6972,17 @@ typename container_input_adapter_factory_impl::container_input_adapter_factory::create(container); } +// specialization for std::string +using string_input_adapter_type = decltype(input_adapter(std::declval())); + #ifndef JSON_NO_IO // Special cases with fast paths inline file_input_adapter input_adapter(std::FILE* file) { + if (file == nullptr) + { + JSON_THROW(parse_error::create(101, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr)); + } return file_input_adapter(file); } @@ -6588,9 +7009,13 @@ template < typename CharT, int >::type = 0 > contiguous_bytes_input_adapter input_adapter(CharT b) { + if (b == nullptr) + { + JSON_THROW(parse_error::create(101, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr)); + } auto length = std::strlen(reinterpret_cast(b)); const auto* ptr = reinterpret_cast(b); - return input_adapter(ptr, ptr + length); + return input_adapter(ptr, ptr + length); // cppcheck-suppress[nullPointerArithmeticRedundantCheck] } template @@ -6636,2383 +7061,2653 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include #include // string +#include // enable_if_t #include // move #include // vector // #include -// #include - -// #include - +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT -NLOHMANN_JSON_NAMESPACE_BEGIN -/*! -@brief SAX interface -This class describes the SAX interface used by @ref nlohmann::json::sax_parse. -Each function is called in different situations while the input is parsed. The -boolean return value informs the parser whether to continue processing the -input. -*/ -template -struct json_sax -{ - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; - - /*! - @brief a null value was read - @return whether parsing should proceed - */ - virtual bool null() = 0; - - /*! - @brief a boolean value was read - @param[in] val boolean value - @return whether parsing should proceed - */ - virtual bool boolean(bool val) = 0; - - /*! - @brief an integer number was read - @param[in] val integer value - @return whether parsing should proceed - */ - virtual bool number_integer(number_integer_t val) = 0; - - /*! - @brief an unsigned integer number was read - @param[in] val unsigned integer value - @return whether parsing should proceed - */ - virtual bool number_unsigned(number_unsigned_t val) = 0; - - /*! - @brief a floating-point number was read - @param[in] val floating-point value - @param[in] s raw token value - @return whether parsing should proceed - */ - virtual bool number_float(number_float_t val, const string_t& s) = 0; +#include // array +#include // localeconv +#include // size_t +#include // snprintf +#include // strtof, strtod, strtold, strtoll, strtoull +#include // initializer_list +#include // char_traits, string +#include // move +#include // vector - /*! - @brief a string value was read - @param[in] val string value - @return whether parsing should proceed - @note It is safe to move the passed string value. - */ - virtual bool string(string_t& val) = 0; +// #include - /*! - @brief a binary value was read - @param[in] val binary value - @return whether parsing should proceed - @note It is safe to move the passed binary value. - */ - virtual bool binary(binary_t& val) = 0; +// #include - /*! - @brief the beginning of an object was read - @param[in] elements number of object elements or -1 if unknown - @return whether parsing should proceed - @note binary formats may report the number of elements - */ - virtual bool start_object(std::size_t elements) = 0; +// #include - /*! - @brief an object key was read - @param[in] val object key - @return whether parsing should proceed - @note It is safe to move the passed string. - */ - virtual bool key(string_t& val) = 0; +// #include - /*! - @brief the end of an object was read - @return whether parsing should proceed - */ - virtual bool end_object() = 0; - /*! - @brief the beginning of an array was read - @param[in] elements number of array elements or -1 if unknown - @return whether parsing should proceed - @note binary formats may report the number of elements - */ - virtual bool start_array(std::size_t elements) = 0; +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ - /*! - @brief the end of an array was read - @return whether parsing should proceed - */ - virtual bool end_array() = 0; +/////////// +// lexer // +/////////// - /*! - @brief a parse error occurred - @param[in] position the position in the input where the error occurs - @param[in] last_token the last read token - @param[in] ex an exception object describing the error - @return whether parsing should proceed (must return false) - */ - virtual bool parse_error(std::size_t position, - const std::string& last_token, - const detail::exception& ex) = 0; +template +class lexer_base +{ + public: + /// token types for the parser + enum class token_type + { + uninitialized, ///< indicating the scanner is uninitialized + literal_true, ///< the `true` literal + literal_false, ///< the `false` literal + literal_null, ///< the `null` literal + value_string, ///< a string -- use get_string() for actual value + value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value + value_integer, ///< a signed integer -- use get_number_integer() for actual value + value_float, ///< an floating point number -- use get_number_float() for actual value + begin_array, ///< the character for array begin `[` + begin_object, ///< the character for object begin `{` + end_array, ///< the character for array end `]` + end_object, ///< the character for object end `}` + name_separator, ///< the name separator `:` + value_separator, ///< the value separator `,` + parse_error, ///< indicating a parse error + end_of_input, ///< indicating the end of the input buffer + literal_or_value ///< a literal or the begin of a value (only for diagnostics) + }; - json_sax() = default; - json_sax(const json_sax&) = default; - json_sax(json_sax&&) noexcept = default; - json_sax& operator=(const json_sax&) = default; - json_sax& operator=(json_sax&&) noexcept = default; - virtual ~json_sax() = default; + /// return name of values of type token_type (only used for errors) + JSON_HEDLEY_RETURNS_NON_NULL + JSON_HEDLEY_CONST + static const char* token_type_name(const token_type t) noexcept + { + switch (t) + { + case token_type::uninitialized: + return ""; + case token_type::literal_true: + return "true literal"; + case token_type::literal_false: + return "false literal"; + case token_type::literal_null: + return "null literal"; + case token_type::value_string: + return "string literal"; + case token_type::value_unsigned: + case token_type::value_integer: + case token_type::value_float: + return "number literal"; + case token_type::begin_array: + return "'['"; + case token_type::begin_object: + return "'{'"; + case token_type::end_array: + return "']'"; + case token_type::end_object: + return "'}'"; + case token_type::name_separator: + return "':'"; + case token_type::value_separator: + return "','"; + case token_type::parse_error: + return ""; + case token_type::end_of_input: + return "end of input"; + case token_type::literal_or_value: + return "'[', '{', or a literal"; + // LCOV_EXCL_START + default: // catch non-enum values + return "unknown token"; + // LCOV_EXCL_STOP + } + } }; - -namespace detail -{ /*! -@brief SAX implementation to create a JSON value from SAX events - -This class implements the @ref json_sax interface and processes the SAX events -to create a JSON value which makes it basically a DOM parser. The structure or -hierarchy of the JSON value is managed by the stack `ref_stack` which contains -a pointer to the respective array or object for each recursion depth. - -After successful parsing, the value that is passed by reference to the -constructor contains the parsed value. +@brief lexical analysis -@tparam BasicJsonType the JSON type +This class organizes the lexical analysis during JSON deserialization. */ -template -class json_sax_dom_parser +template +class lexer : public lexer_base { - public: using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; + using char_type = typename InputAdapterType::char_type; + using char_int_type = typename char_traits::int_type; - /*! - @param[in,out] r reference to a JSON value that is manipulated while - parsing - @param[in] allow_exceptions_ whether parse errors yield exceptions - */ - explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true) - : root(r), allow_exceptions(allow_exceptions_) + public: + using token_type = typename lexer_base::token_type; + + explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept + : ia(std::move(adapter)) + , ignore_comments(ignore_comments_) + , decimal_point_char(static_cast(get_decimal_point())) {} - // make class move-only - json_sax_dom_parser(const json_sax_dom_parser&) = delete; - json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) - json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete; - json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) - ~json_sax_dom_parser() = default; + // deleted because of pointer members + lexer(const lexer&) = delete; + lexer(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + lexer& operator=(lexer&) = delete; + lexer& operator=(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~lexer() = default; - bool null() + private: + ///////////////////// + // locales + ///////////////////// + + /// return the locale-dependent decimal point + JSON_HEDLEY_PURE + static char get_decimal_point() noexcept { - handle_value(nullptr); - return true; + const auto* loc = localeconv(); + JSON_ASSERT(loc != nullptr); + return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); } - bool boolean(bool val) - { - handle_value(val); - return true; - } - - bool number_integer(number_integer_t val) - { - handle_value(val); - return true; - } - - bool number_unsigned(number_unsigned_t val) - { - handle_value(val); - return true; - } + ///////////////////// + // scan functions + ///////////////////// - bool number_float(number_float_t val, const string_t& /*unused*/) - { - handle_value(val); - return true; - } + /*! + @brief get codepoint from 4 hex characters following `\u` - bool string(string_t& val) - { - handle_value(val); - return true; - } + For input "\u c1 c2 c3 c4" the codepoint is: + (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4 + = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0) - bool binary(binary_t& val) - { - handle_value(std::move(val)); - return true; - } + Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f' + must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The + conversion is done by subtracting the offset (0x30, 0x37, and 0x57) + between the ASCII value of the character and the desired integer value. - bool start_object(std::size_t len) + @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or + non-hex character) + */ + int get_codepoint() { - ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); + // this function only makes sense after reading `\u` + JSON_ASSERT(current == 'u'); + int codepoint = 0; - if (JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) + const auto factors = { 12u, 8u, 4u, 0u }; + for (const auto factor : factors) { - JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back())); + get(); + + if (current >= '0' && current <= '9') + { + codepoint += static_cast((static_cast(current) - 0x30u) << factor); + } + else if (current >= 'A' && current <= 'F') + { + codepoint += static_cast((static_cast(current) - 0x37u) << factor); + } + else if (current >= 'a' && current <= 'f') + { + codepoint += static_cast((static_cast(current) - 0x57u) << factor); + } + else + { + return -1; + } } - return true; + JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF); + return codepoint; } - bool key(string_t& val) - { - JSON_ASSERT(!ref_stack.empty()); - JSON_ASSERT(ref_stack.back()->is_object()); + /*! + @brief check if the next byte(s) are inside a given range - // add null at given key and store the reference for later - object_element = &(ref_stack.back()->m_data.m_value.object->operator[](val)); - return true; - } + Adds the current byte and, for each passed range, reads a new byte and + checks if it is inside the range. If a violation was detected, set up an + error message and return false. Otherwise, return true. - bool end_object() - { - JSON_ASSERT(!ref_stack.empty()); - JSON_ASSERT(ref_stack.back()->is_object()); + @param[in] ranges list of integers; interpreted as list of pairs of + inclusive lower and upper bound, respectively - ref_stack.back()->set_parents(); - ref_stack.pop_back(); - return true; - } + @pre The passed list @a ranges must have 2, 4, or 6 elements; that is, + 1, 2, or 3 pairs. This precondition is enforced by an assertion. - bool start_array(std::size_t len) + @return true if and only if no range violation was detected + */ + bool next_byte_in_range(std::initializer_list ranges) { - ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); + JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6); + add(current); - if (JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) + for (auto range = ranges.begin(); range != ranges.end(); ++range) { - JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back())); + get(); + if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) // NOLINT(bugprone-inc-dec-in-conditions) + { + add(current); + } + else + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return false; + } } return true; } - bool end_array() - { - JSON_ASSERT(!ref_stack.empty()); - JSON_ASSERT(ref_stack.back()->is_array()); - - ref_stack.back()->set_parents(); - ref_stack.pop_back(); - return true; - } + /*! + @brief scan a string literal - template - bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, - const Exception& ex) - { - errored = true; - static_cast(ex); - if (allow_exceptions) - { - JSON_THROW(ex); - } - return false; - } + This function scans a string according to Sect. 7 of RFC 8259. While + scanning, bytes are escaped and copied into buffer token_buffer. Then the + function returns successfully, token_buffer is *not* null-terminated (as it + may contain \0 bytes), and token_buffer.size() is the number of bytes in the + string. - constexpr bool is_errored() const - { - return errored; - } + @return token_type::value_string if string could be successfully scanned, + token_type::parse_error otherwise - private: - /*! - @invariant If the ref stack is empty, then the passed value will be the new - root. - @invariant If the ref stack contains a value, then it is an array or an - object to which we can add elements + @note In case of errors, variable error_message contains a textual + description. */ - template - JSON_HEDLEY_RETURNS_NON_NULL - BasicJsonType* handle_value(Value&& v) + token_type scan_string() { - if (ref_stack.empty()) - { - root = BasicJsonType(std::forward(v)); - return &root; - } + // reset token_buffer (ignore opening quote) + reset(); - JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); + // we entered the function by reading an open quote + JSON_ASSERT(current == '\"'); - if (ref_stack.back()->is_array()) + while (true) { - ref_stack.back()->m_data.m_value.array->emplace_back(std::forward(v)); - return &(ref_stack.back()->m_data.m_value.array->back()); - } - - JSON_ASSERT(ref_stack.back()->is_object()); - JSON_ASSERT(object_element); - *object_element = BasicJsonType(std::forward(v)); - return object_element; - } + // get the next character + switch (get()) + { + // end of file while parsing the string + case char_traits::eof(): + { + error_message = "invalid string: missing closing quote"; + return token_type::parse_error; + } - /// the parsed JSON value - BasicJsonType& root; - /// stack to model hierarchy of values - std::vector ref_stack {}; - /// helper to hold the reference for the next object element - BasicJsonType* object_element = nullptr; - /// whether a syntax error occurred - bool errored = false; - /// whether to throw exceptions in case of errors - const bool allow_exceptions = true; -}; + // closing quote + case '\"': + { + return token_type::value_string; + } -template -class json_sax_dom_callback_parser -{ - public: - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; - using parser_callback_t = typename BasicJsonType::parser_callback_t; - using parse_event_t = typename BasicJsonType::parse_event_t; + // escapes + case '\\': + { + switch (get()) + { + // quotation mark + case '\"': + add('\"'); + break; + // reverse solidus + case '\\': + add('\\'); + break; + // solidus + case '/': + add('/'); + break; + // backspace + case 'b': + add('\b'); + break; + // form feed + case 'f': + add('\f'); + break; + // line feed + case 'n': + add('\n'); + break; + // carriage return + case 'r': + add('\r'); + break; + // tab + case 't': + add('\t'); + break; - json_sax_dom_callback_parser(BasicJsonType& r, - const parser_callback_t cb, - const bool allow_exceptions_ = true) - : root(r), callback(cb), allow_exceptions(allow_exceptions_) - { - keep_stack.push_back(true); - } - - // make class move-only - json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete; - json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) - json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete; - json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) - ~json_sax_dom_callback_parser() = default; + // unicode escapes + case 'u': + { + const int codepoint1 = get_codepoint(); + int codepoint = codepoint1; // start with codepoint1 - bool null() - { - handle_value(nullptr); - return true; - } + if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } - bool boolean(bool val) - { - handle_value(val); - return true; - } + // check if code point is a high surrogate + if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF) + { + // expect next \uxxxx entry + if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u')) + { + const int codepoint2 = get_codepoint(); - bool number_integer(number_integer_t val) - { - handle_value(val); - return true; - } + if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } - bool number_unsigned(number_unsigned_t val) - { - handle_value(val); - return true; - } + // check if codepoint2 is a low surrogate + if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF)) + { + // overwrite codepoint + codepoint = static_cast( + // high surrogate occupies the most significant 22 bits + (static_cast(codepoint1) << 10u) + // low surrogate occupies the least significant 15 bits + + static_cast(codepoint2) + // there is still the 0xD800, 0xDC00, and 0x10000 noise + // in the result, so we have to subtract with: + // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 + - 0x35FDC00u); + } + else + { + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF)) + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; + return token_type::parse_error; + } + } - bool number_float(number_float_t val, const string_t& /*unused*/) - { - handle_value(val); - return true; - } + // the result of the above calculation yields a proper codepoint + JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF); - bool string(string_t& val) - { - handle_value(val); - return true; - } + // translate codepoint into bytes + if (codepoint < 0x80) + { + // 1-byte characters: 0xxxxxxx (ASCII) + add(static_cast(codepoint)); + } + else if (codepoint <= 0x7FF) + { + // 2-byte characters: 110xxxxx 10xxxxxx + add(static_cast(0xC0u | (static_cast(codepoint) >> 6u))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else if (codepoint <= 0xFFFF) + { + // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx + add(static_cast(0xE0u | (static_cast(codepoint) >> 12u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else + { + // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + add(static_cast(0xF0u | (static_cast(codepoint) >> 18u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 12u) & 0x3Fu))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } - bool binary(binary_t& val) - { - handle_value(std::move(val)); - return true; - } + break; + } - bool start_object(std::size_t len) - { - // check callback for object start - const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::object_start, discarded); - keep_stack.push_back(keep); + // other characters after escape + default: + error_message = "invalid string: forbidden character after backslash"; + return token_type::parse_error; + } - auto val = handle_value(BasicJsonType::value_t::object, true); - ref_stack.push_back(val.second); + break; + } - // check object limit - if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) - { - JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back())); - } + // invalid control characters + case 0x00: + { + error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000"; + return token_type::parse_error; + } - return true; - } + case 0x01: + { + error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001"; + return token_type::parse_error; + } - bool key(string_t& val) - { - BasicJsonType k = BasicJsonType(val); + case 0x02: + { + error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002"; + return token_type::parse_error; + } - // check callback for key - const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::key, k); - key_keep_stack.push_back(keep); + case 0x03: + { + error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003"; + return token_type::parse_error; + } - // add discarded value at given key and store the reference for later - if (keep && ref_stack.back()) - { - object_element = &(ref_stack.back()->m_data.m_value.object->operator[](val) = discarded); - } + case 0x04: + { + error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004"; + return token_type::parse_error; + } - return true; - } + case 0x05: + { + error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005"; + return token_type::parse_error; + } - bool end_object() - { - if (ref_stack.back()) - { - if (!callback(static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) - { - // discard object - *ref_stack.back() = discarded; - } - else - { - ref_stack.back()->set_parents(); - } - } + case 0x06: + { + error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006"; + return token_type::parse_error; + } - JSON_ASSERT(!ref_stack.empty()); - JSON_ASSERT(!keep_stack.empty()); - ref_stack.pop_back(); - keep_stack.pop_back(); + case 0x07: + { + error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007"; + return token_type::parse_error; + } - if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured()) - { - // remove discarded value - for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it) - { - if (it->is_discarded()) + case 0x08: { - ref_stack.back()->erase(it); - break; + error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b"; + return token_type::parse_error; } - } - } - return true; - } + case 0x09: + { + error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t"; + return token_type::parse_error; + } - bool start_array(std::size_t len) - { - const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::array_start, discarded); - keep_stack.push_back(keep); + case 0x0A: + { + error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n"; + return token_type::parse_error; + } - auto val = handle_value(BasicJsonType::value_t::array, true); - ref_stack.push_back(val.second); + case 0x0B: + { + error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B"; + return token_type::parse_error; + } - // check array limit - if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) - { - JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back())); - } - - return true; - } - - bool end_array() - { - bool keep = true; - - if (ref_stack.back()) - { - keep = callback(static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); - if (keep) - { - ref_stack.back()->set_parents(); - } - else - { - // discard array - *ref_stack.back() = discarded; - } - } - - JSON_ASSERT(!ref_stack.empty()); - JSON_ASSERT(!keep_stack.empty()); - ref_stack.pop_back(); - keep_stack.pop_back(); - - // remove discarded value - if (!keep && !ref_stack.empty() && ref_stack.back()->is_array()) - { - ref_stack.back()->m_data.m_value.array->pop_back(); - } - - return true; - } + case 0x0C: + { + error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f"; + return token_type::parse_error; + } - template - bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, - const Exception& ex) - { - errored = true; - static_cast(ex); - if (allow_exceptions) - { - JSON_THROW(ex); - } - return false; - } + case 0x0D: + { + error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r"; + return token_type::parse_error; + } - constexpr bool is_errored() const - { - return errored; - } + case 0x0E: + { + error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E"; + return token_type::parse_error; + } - private: - /*! - @param[in] v value to add to the JSON value we build during parsing - @param[in] skip_callback whether we should skip calling the callback - function; this is required after start_array() and - start_object() SAX events, because otherwise we would call the - callback function with an empty array or object, respectively. + case 0x0F: + { + error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F"; + return token_type::parse_error; + } - @invariant If the ref stack is empty, then the passed value will be the new - root. - @invariant If the ref stack contains a value, then it is an array or an - object to which we can add elements + case 0x10: + { + error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010"; + return token_type::parse_error; + } - @return pair of boolean (whether value should be kept) and pointer (to the - passed value in the ref_stack hierarchy; nullptr if not kept) - */ - template - std::pair handle_value(Value&& v, const bool skip_callback = false) - { - JSON_ASSERT(!keep_stack.empty()); + case 0x11: + { + error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011"; + return token_type::parse_error; + } - // do not handle this value if we know it would be added to a discarded - // container - if (!keep_stack.back()) - { - return {false, nullptr}; - } + case 0x12: + { + error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012"; + return token_type::parse_error; + } - // create value - auto value = BasicJsonType(std::forward(v)); + case 0x13: + { + error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013"; + return token_type::parse_error; + } - // check callback - const bool keep = skip_callback || callback(static_cast(ref_stack.size()), parse_event_t::value, value); + case 0x14: + { + error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014"; + return token_type::parse_error; + } - // do not handle this value if we just learnt it shall be discarded - if (!keep) - { - return {false, nullptr}; - } + case 0x15: + { + error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015"; + return token_type::parse_error; + } - if (ref_stack.empty()) - { - root = std::move(value); - return {true, & root}; - } + case 0x16: + { + error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016"; + return token_type::parse_error; + } - // skip this value if we already decided to skip the parent - // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360) - if (!ref_stack.back()) - { - return {false, nullptr}; - } + case 0x17: + { + error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017"; + return token_type::parse_error; + } - // we now only expect arrays and objects - JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); + case 0x18: + { + error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018"; + return token_type::parse_error; + } - // array - if (ref_stack.back()->is_array()) - { - ref_stack.back()->m_data.m_value.array->emplace_back(std::move(value)); - return {true, & (ref_stack.back()->m_data.m_value.array->back())}; - } + case 0x19: + { + error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019"; + return token_type::parse_error; + } - // object - JSON_ASSERT(ref_stack.back()->is_object()); - // check if we should store an element for the current key - JSON_ASSERT(!key_keep_stack.empty()); - const bool store_element = key_keep_stack.back(); - key_keep_stack.pop_back(); + case 0x1A: + { + error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A"; + return token_type::parse_error; + } - if (!store_element) - { - return {false, nullptr}; - } + case 0x1B: + { + error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B"; + return token_type::parse_error; + } - JSON_ASSERT(object_element); - *object_element = std::move(value); - return {true, object_element}; - } + case 0x1C: + { + error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C"; + return token_type::parse_error; + } - /// the parsed JSON value - BasicJsonType& root; - /// stack to model hierarchy of values - std::vector ref_stack {}; - /// stack to manage which values to keep - std::vector keep_stack {}; // NOLINT(readability-redundant-member-init) - /// stack to manage which object keys to keep - std::vector key_keep_stack {}; // NOLINT(readability-redundant-member-init) - /// helper to hold the reference for the next object element - BasicJsonType* object_element = nullptr; - /// whether a syntax error occurred - bool errored = false; - /// callback function - const parser_callback_t callback = nullptr; - /// whether to throw exceptions in case of errors - const bool allow_exceptions = true; - /// a discarded value for the callback - BasicJsonType discarded = BasicJsonType::value_t::discarded; -}; + case 0x1D: + { + error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D"; + return token_type::parse_error; + } -template -class json_sax_acceptor -{ - public: - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; + case 0x1E: + { + error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E"; + return token_type::parse_error; + } - bool null() - { - return true; - } + case 0x1F: + { + error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F"; + return token_type::parse_error; + } - bool boolean(bool /*unused*/) - { - return true; - } + // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace)) + case 0x20: + case 0x21: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + { + add(current); + break; + } + + // U+0080..U+07FF: bytes C2..DF 80..BF + case 0xC2: + case 0xC3: + case 0xC4: + case 0xC5: + case 0xC6: + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD5: + case 0xD6: + case 0xD7: + case 0xD8: + case 0xD9: + case 0xDA: + case 0xDB: + case 0xDC: + case 0xDD: + case 0xDE: + case 0xDF: + { + if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF}))) + { + return token_type::parse_error; + } + break; + } + + // U+0800..U+0FFF: bytes E0 A0..BF 80..BF + case 0xE0: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF + // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xEE: + case 0xEF: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+D000..U+D7FF: bytes ED 80..9F 80..BF + case 0xED: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF + case 0xF0: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF + case 0xF1: + case 0xF2: + case 0xF3: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF + case 0xF4: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // the remaining bytes (80..C1 and F5..FF) are ill-formed + default: + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; + } + } + } + } + + /*! + * @brief scan a comment + * @return whether comment could be scanned successfully + */ + bool scan_comment() + { + switch (get()) + { + // single-line comments skip input until a newline or EOF is read + case '/': + { + while (true) + { + switch (get()) + { + case '\n': + case '\r': + case char_traits::eof(): + case '\0': + return true; + + default: + break; + } + } + } + + // multi-line comments skip input until */ is read + case '*': + { + while (true) + { + switch (get()) + { + case char_traits::eof(): + case '\0': + { + error_message = "invalid comment; missing closing '*/'"; + return false; + } + + case '*': + { + switch (get()) + { + case '/': + return true; + + default: + { + unget(); + continue; + } + } + } + + default: + continue; + } + } + } + + // unexpected character after reading '/' + default: + { + error_message = "invalid comment; expecting '/' or '*' after '/'"; + return false; + } + } + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(float& f, const char* str, char** endptr) noexcept + { + f = std::strtof(str, endptr); + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(double& f, const char* str, char** endptr) noexcept + { + f = std::strtod(str, endptr); + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(long double& f, const char* str, char** endptr) noexcept + { + f = std::strtold(str, endptr); + } + + /*! + @brief scan a number literal + + This function scans a string according to Sect. 6 of RFC 8259. + + The function is realized with a deterministic finite state machine derived + from the grammar described in RFC 8259. Starting in state "init", the + input is read and used to determined the next state. Only state "done" + accepts the number. State "error" is a trap state to model errors. In the + table below, "anything" means any character but the ones listed before. + + state | 0 | 1-9 | e E | + | - | . | anything + ---------|----------|----------|----------|---------|---------|----------|----------- + init | zero | any1 | [error] | [error] | minus | [error] | [error] + minus | zero | any1 | [error] | [error] | [error] | [error] | [error] + zero | done | done | exponent | done | done | decimal1 | done + any1 | any1 | any1 | exponent | done | done | decimal1 | done + decimal1 | decimal2 | decimal2 | [error] | [error] | [error] | [error] | [error] + decimal2 | decimal2 | decimal2 | exponent | done | done | done | done + exponent | any2 | any2 | [error] | sign | sign | [error] | [error] + sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] + any2 | any2 | any2 | done | done | done | done | done + + The state machine is realized with one label per state (prefixed with + "scan_number_") and `goto` statements between them. The state machine + contains cycles, but any cycle can be left when EOF is read. Therefore, + the function is guaranteed to terminate. + + During scanning, the read bytes are stored in token_buffer. This string is + then converted to a signed integer, an unsigned integer, or a + floating-point number. + + @return token_type::value_unsigned, token_type::value_integer, or + token_type::value_float if number could be successfully scanned, + token_type::parse_error otherwise + + @note The scanner is independent of the current locale. Internally, the + locale's decimal point is used instead of `.` to work with the + locale-dependent converters. + */ + token_type scan_number() // lgtm [cpp/use-of-goto] `goto` is used in this function to implement the number-parsing state machine described above. By design, any finite input will eventually reach the "done" state or return token_type::parse_error. In each intermediate state, 1 byte of the input is appended to the token_buffer vector, and only the already initialized variables token_buffer, number_type, and error_message are manipulated. + { + // reset token_buffer to store the number's bytes + reset(); + + // the type of the parsed number; initially set to unsigned; will be + // changed if minus sign, decimal point, or exponent is read + token_type number_type = token_type::value_unsigned; - bool number_integer(number_integer_t /*unused*/) - { - return true; - } + // state (init): we just found out we need to scan a number + switch (current) + { + case '-': + { + add(current); + goto scan_number_minus; + } - bool number_unsigned(number_unsigned_t /*unused*/) - { - return true; - } + case '0': + { + add(current); + goto scan_number_zero; + } - bool number_float(number_float_t /*unused*/, const string_t& /*unused*/) - { - return true; - } + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } - bool string(string_t& /*unused*/) - { - return true; - } + // all other characters are rejected outside scan_number() + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } - bool binary(binary_t& /*unused*/) - { - return true; - } +scan_number_minus: + // state: we just parsed a leading minus sign + number_type = token_type::value_integer; + switch (get()) + { + case '0': + { + add(current); + goto scan_number_zero; + } - bool start_object(std::size_t /*unused*/ = static_cast(-1)) - { - return true; - } + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } - bool key(string_t& /*unused*/) - { - return true; - } + default: + { + error_message = "invalid number; expected digit after '-'"; + return token_type::parse_error; + } + } - bool end_object() - { - return true; - } +scan_number_zero: + // state: we just parse a zero (maybe with a leading minus sign) + switch (get()) + { + case '.': + { + add(decimal_point_char); + decimal_point_position = token_buffer.size() - 1; + goto scan_number_decimal1; + } - bool start_array(std::size_t /*unused*/ = static_cast(-1)) - { - return true; - } + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } - bool end_array() - { - return true; - } + default: + goto scan_number_done; + } - bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/) - { - return false; - } -}; +scan_number_any1: + // state: we just parsed a number 0-9 (maybe with a leading minus sign) + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END + case '.': + { + add(decimal_point_char); + decimal_point_position = token_buffer.size() - 1; + goto scan_number_decimal1; + } -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann -// SPDX-License-Identifier: MIT + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + default: + goto scan_number_done; + } +scan_number_decimal1: + // state: we just parsed a decimal point + number_type = token_type::value_float; + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } -#include // array -#include // localeconv -#include // size_t -#include // snprintf -#include // strtof, strtod, strtold, strtoll, strtoull -#include // initializer_list -#include // char_traits, string -#include // move -#include // vector + default: + { + error_message = "invalid number; expected digit after '.'"; + return token_type::parse_error; + } + } + +scan_number_decimal2: + // we just parsed at least one number after a decimal point + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_exponent: + // we just parsed an exponent + number_type = token_type::value_float; + switch (get()) + { + case '+': + case '-': + { + add(current); + goto scan_number_sign; + } + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = + "invalid number; expected '+', '-', or digit after exponent"; + return token_type::parse_error; + } + } -// #include +scan_number_sign: + // we just parsed an exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } -// #include + default: + { + error_message = "invalid number; expected digit after exponent sign"; + return token_type::parse_error; + } + } -// #include +scan_number_any2: + // we just parsed a number after the exponent or exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } -// #include + default: + goto scan_number_done; + } +scan_number_done: + // unget the character after the number (we only read it to know that + // we are done scanning a number) + unget(); -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ + char* endptr = nullptr; // NOLINT(misc-const-correctness,cppcoreguidelines-pro-type-vararg,hicpp-vararg) + errno = 0; -/////////// -// lexer // -/////////// + // try to parse integers first and fall back to floats + if (number_type == token_type::value_unsigned) + { + const auto x = std::strtoull(token_buffer.data(), &endptr, 10); -template -class lexer_base -{ - public: - /// token types for the parser - enum class token_type - { - uninitialized, ///< indicating the scanner is uninitialized - literal_true, ///< the `true` literal - literal_false, ///< the `false` literal - literal_null, ///< the `null` literal - value_string, ///< a string -- use get_string() for actual value - value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value - value_integer, ///< a signed integer -- use get_number_integer() for actual value - value_float, ///< an floating point number -- use get_number_float() for actual value - begin_array, ///< the character for array begin `[` - begin_object, ///< the character for object begin `{` - end_array, ///< the character for array end `]` - end_object, ///< the character for object end `}` - name_separator, ///< the name separator `:` - value_separator, ///< the value separator `,` - parse_error, ///< indicating a parse error - end_of_input, ///< indicating the end of the input buffer - literal_or_value ///< a literal or the begin of a value (only for diagnostics) - }; + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); - /// return name of values of type token_type (only used for errors) - JSON_HEDLEY_RETURNS_NON_NULL - JSON_HEDLEY_CONST - static const char* token_type_name(const token_type t) noexcept - { - switch (t) - { - case token_type::uninitialized: - return ""; - case token_type::literal_true: - return "true literal"; - case token_type::literal_false: - return "false literal"; - case token_type::literal_null: - return "null literal"; - case token_type::value_string: - return "string literal"; - case token_type::value_unsigned: - case token_type::value_integer: - case token_type::value_float: - return "number literal"; - case token_type::begin_array: - return "'['"; - case token_type::begin_object: - return "'{'"; - case token_type::end_array: - return "']'"; - case token_type::end_object: - return "'}'"; - case token_type::name_separator: - return "':'"; - case token_type::value_separator: - return "','"; - case token_type::parse_error: - return ""; - case token_type::end_of_input: - return "end of input"; - case token_type::literal_or_value: - return "'[', '{', or a literal"; - // LCOV_EXCL_START - default: // catch non-enum values - return "unknown token"; - // LCOV_EXCL_STOP + if (errno != ERANGE) + { + value_unsigned = static_cast(x); + if (value_unsigned == x) + { + return token_type::value_unsigned; + } + } } - } -}; -/*! -@brief lexical analysis - -This class organizes the lexical analysis during JSON deserialization. -*/ -template -class lexer : public lexer_base -{ - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using char_type = typename InputAdapterType::char_type; - using char_int_type = typename char_traits::int_type; + else if (number_type == token_type::value_integer) + { + const auto x = std::strtoll(token_buffer.data(), &endptr, 10); - public: - using token_type = typename lexer_base::token_type; + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); - explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept - : ia(std::move(adapter)) - , ignore_comments(ignore_comments_) - , decimal_point_char(static_cast(get_decimal_point())) - {} + if (errno != ERANGE) + { + value_integer = static_cast(x); + if (value_integer == x) + { + return token_type::value_integer; + } + } + } - // delete because of pointer members - lexer(const lexer&) = delete; - lexer(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) - lexer& operator=(lexer&) = delete; - lexer& operator=(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) - ~lexer() = default; + // this code is reached if we parse a floating-point number or if an + // integer conversion above failed + strtof(value_float, token_buffer.data(), &endptr); - private: - ///////////////////// - // locales - ///////////////////// + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); - /// return the locale-dependent decimal point - JSON_HEDLEY_PURE - static char get_decimal_point() noexcept - { - const auto* loc = localeconv(); - JSON_ASSERT(loc != nullptr); - return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); + return token_type::value_float; } - ///////////////////// - // scan functions - ///////////////////// - /*! - @brief get codepoint from 4 hex characters following `\u` - - For input "\u c1 c2 c3 c4" the codepoint is: - (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4 - = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0) - - Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f' - must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The - conversion is done by subtracting the offset (0x30, 0x37, and 0x57) - between the ASCII value of the character and the desired integer value. - - @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or - non-hex character) + @param[in] literal_text the literal text to expect + @param[in] length the length of the passed literal text + @param[in] return_type the token type to return on success */ - int get_codepoint() + JSON_HEDLEY_NON_NULL(2) + token_type scan_literal(const char_type* literal_text, const std::size_t length, + token_type return_type) { - // this function only makes sense after reading `\u` - JSON_ASSERT(current == 'u'); - int codepoint = 0; - - const auto factors = { 12u, 8u, 4u, 0u }; - for (const auto factor : factors) + JSON_ASSERT(char_traits::to_char_type(current) == literal_text[0]); + for (std::size_t i = 1; i < length; ++i) { - get(); - - if (current >= '0' && current <= '9') - { - codepoint += static_cast((static_cast(current) - 0x30u) << factor); - } - else if (current >= 'A' && current <= 'F') - { - codepoint += static_cast((static_cast(current) - 0x37u) << factor); - } - else if (current >= 'a' && current <= 'f') - { - codepoint += static_cast((static_cast(current) - 0x57u) << factor); - } - else + if (JSON_HEDLEY_UNLIKELY(char_traits::to_char_type(get()) != literal_text[i])) { - return -1; + error_message = "invalid literal"; + return token_type::parse_error; } } - - JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF); - return codepoint; + return return_type; } - /*! - @brief check if the next byte(s) are inside a given range + ///////////////////// + // input management + ///////////////////// - Adds the current byte and, for each passed range, reads a new byte and - checks if it is inside the range. If a violation was detected, set up an - error message and return false. Otherwise, return true. + /// reset token_buffer; current character is beginning of token + void reset() noexcept + { + token_buffer.clear(); + token_string.clear(); + decimal_point_position = std::string::npos; + token_string.push_back(char_traits::to_char_type(current)); + } - @param[in] ranges list of integers; interpreted as list of pairs of - inclusive lower and upper bound, respectively + /* + @brief get next character from the input - @pre The passed list @a ranges must have 2, 4, or 6 elements; that is, - 1, 2, or 3 pairs. This precondition is enforced by an assertion. + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a + `char_traits::eof()` in that case. Stores the scanned characters + for use in error messages. - @return true if and only if no range violation was detected + @return character read from the input */ - bool next_byte_in_range(std::initializer_list ranges) + char_int_type get() { - JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6); - add(current); + ++position.chars_read_total; + ++position.chars_read_current_line; - for (auto range = ranges.begin(); range != ranges.end(); ++range) + if (next_unget) { - get(); - if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) // NOLINT(bugprone-inc-dec-in-conditions) - { - add(current); - } - else - { - error_message = "invalid string: ill-formed UTF-8 byte"; - return false; - } + // only reset the next_unget variable and work with current + next_unget = false; + } + else + { + current = ia.get_character(); } - return true; - } + if (JSON_HEDLEY_LIKELY(current != char_traits::eof())) + { + token_string.push_back(char_traits::to_char_type(current)); + } - /*! - @brief scan a string literal + if (current == '\n') + { + ++position.lines_read; + position.chars_read_current_line = 0; + } - This function scans a string according to Sect. 7 of RFC 8259. While - scanning, bytes are escaped and copied into buffer token_buffer. Then the - function returns successfully, token_buffer is *not* null-terminated (as it - may contain \0 bytes), and token_buffer.size() is the number of bytes in the - string. + return current; + } - @return token_type::value_string if string could be successfully scanned, - token_type::parse_error otherwise + /*! + @brief unget current character (read it again on next get) - @note In case of errors, variable error_message contains a textual - description. + We implement unget by setting variable next_unget to true. The input is not + changed - we just simulate ungetting by modifying chars_read_total, + chars_read_current_line, and token_string. The next call to get() will + behave as if the unget character is read again. */ - token_type scan_string() + void unget() { - // reset token_buffer (ignore opening quote) - reset(); + next_unget = true; - // we entered the function by reading an open quote - JSON_ASSERT(current == '\"'); + --position.chars_read_total; - while (true) + // in case we "unget" a newline, we have to also decrement the lines_read + if (position.chars_read_current_line == 0) { - // get next character - switch (get()) + if (position.lines_read > 0) { - // end of file while parsing string - case char_traits::eof(): - { - error_message = "invalid string: missing closing quote"; - return token_type::parse_error; - } - - // closing quote - case '\"': - { - return token_type::value_string; - } - - // escapes - case '\\': - { - switch (get()) - { - // quotation mark - case '\"': - add('\"'); - break; - // reverse solidus - case '\\': - add('\\'); - break; - // solidus - case '/': - add('/'); - break; - // backspace - case 'b': - add('\b'); - break; - // form feed - case 'f': - add('\f'); - break; - // line feed - case 'n': - add('\n'); - break; - // carriage return - case 'r': - add('\r'); - break; - // tab - case 't': - add('\t'); - break; - - // unicode escapes - case 'u': - { - const int codepoint1 = get_codepoint(); - int codepoint = codepoint1; // start with codepoint1 - - if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1)) - { - error_message = "invalid string: '\\u' must be followed by 4 hex digits"; - return token_type::parse_error; - } + --position.lines_read; + } + } + else + { + --position.chars_read_current_line; + } - // check if code point is a high surrogate - if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF) - { - // expect next \uxxxx entry - if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u')) - { - const int codepoint2 = get_codepoint(); + if (JSON_HEDLEY_LIKELY(current != char_traits::eof())) + { + JSON_ASSERT(!token_string.empty()); + token_string.pop_back(); + } + } - if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1)) - { - error_message = "invalid string: '\\u' must be followed by 4 hex digits"; - return token_type::parse_error; - } + /// add a character to token_buffer + void add(char_int_type c) + { + token_buffer.push_back(static_cast(c)); + } - // check if codepoint2 is a low surrogate - if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF)) - { - // overwrite codepoint - codepoint = static_cast( - // high surrogate occupies the most significant 22 bits - (static_cast(codepoint1) << 10u) - // low surrogate occupies the least significant 15 bits - + static_cast(codepoint2) - // there is still the 0xD800, 0xDC00 and 0x10000 noise - // in the result, so we have to subtract with: - // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 - - 0x35FDC00u); - } - else - { - error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; - return token_type::parse_error; - } - } - else - { - error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; - return token_type::parse_error; - } - } - else - { - if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF)) - { - error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; - return token_type::parse_error; - } - } + public: + ///////////////////// + // value getters + ///////////////////// - // result of the above calculation yields a proper codepoint - JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF); + /// return integer value + constexpr number_integer_t get_number_integer() const noexcept + { + return value_integer; + } - // translate codepoint into bytes - if (codepoint < 0x80) - { - // 1-byte characters: 0xxxxxxx (ASCII) - add(static_cast(codepoint)); - } - else if (codepoint <= 0x7FF) - { - // 2-byte characters: 110xxxxx 10xxxxxx - add(static_cast(0xC0u | (static_cast(codepoint) >> 6u))); - add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); - } - else if (codepoint <= 0xFFFF) - { - // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx - add(static_cast(0xE0u | (static_cast(codepoint) >> 12u))); - add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); - add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); - } - else - { - // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - add(static_cast(0xF0u | (static_cast(codepoint) >> 18u))); - add(static_cast(0x80u | ((static_cast(codepoint) >> 12u) & 0x3Fu))); - add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); - add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); - } + /// return unsigned integer value + constexpr number_unsigned_t get_number_unsigned() const noexcept + { + return value_unsigned; + } - break; - } + /// return floating-point value + constexpr number_float_t get_number_float() const noexcept + { + return value_float; + } - // other characters after escape - default: - error_message = "invalid string: forbidden character after backslash"; - return token_type::parse_error; - } + /// return current string value (implicitly resets the token; useful only once) + string_t& get_string() + { + // translate decimal points from locale back to '.' (#4084) + if (decimal_point_char != '.' && decimal_point_position != std::string::npos) + { + token_buffer[decimal_point_position] = '.'; + } + return token_buffer; + } - break; - } + ///////////////////// + // diagnostics + ///////////////////// - // invalid control characters - case 0x00: - { - error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000"; - return token_type::parse_error; - } + /// return position of last read token + constexpr position_t get_position() const noexcept + { + return position; + } - case 0x01: - { - error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001"; - return token_type::parse_error; - } + /// return the last read token (for errors only). Will never contain EOF + /// (an arbitrary value that is not a valid char value, often -1), because + /// 255 may legitimately occur. May contain NUL, which should be escaped. + std::string get_token_string() const + { + // escape control characters + std::string result; + for (const auto c : token_string) + { + if (static_cast(c) <= '\x1F') + { + // escape control characters + std::array cs{{}}; + static_cast((std::snprintf)(cs.data(), cs.size(), "", static_cast(c))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + result += cs.data(); + } + else + { + // add character as is + result.push_back(static_cast(c)); + } + } - case 0x02: - { - error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002"; - return token_type::parse_error; - } + return result; + } - case 0x03: - { - error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003"; - return token_type::parse_error; - } + /// return syntax error message + JSON_HEDLEY_RETURNS_NON_NULL + constexpr const char* get_error_message() const noexcept + { + return error_message; + } - case 0x04: - { - error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004"; - return token_type::parse_error; - } + ///////////////////// + // actual scanner + ///////////////////// - case 0x05: - { - error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005"; - return token_type::parse_error; - } + /*! + @brief skip the UTF-8 byte order mark + @return true iff there is no BOM or the correct BOM has been skipped + */ + bool skip_bom() + { + if (get() == 0xEF) + { + // check if we completely parse the BOM + return get() == 0xBB && get() == 0xBF; + } - case 0x06: - { - error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006"; - return token_type::parse_error; - } + // the first character is not the beginning of the BOM; unget it to + // process is later + unget(); + return true; + } - case 0x07: - { - error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007"; - return token_type::parse_error; - } + void skip_whitespace() + { + do + { + get(); + } + while (current == ' ' || current == '\t' || current == '\n' || current == '\r'); + } - case 0x08: - { - error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b"; - return token_type::parse_error; - } + token_type scan() + { + // initially, skip the BOM + if (position.chars_read_total == 0 && !skip_bom()) + { + error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given"; + return token_type::parse_error; + } - case 0x09: - { - error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t"; - return token_type::parse_error; - } + // read the next character and ignore whitespace + skip_whitespace(); - case 0x0A: - { - error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n"; - return token_type::parse_error; - } + // ignore comments + while (ignore_comments && current == '/') + { + if (!scan_comment()) + { + return token_type::parse_error; + } - case 0x0B: - { - error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B"; - return token_type::parse_error; - } + // skip following whitespace + skip_whitespace(); + } - case 0x0C: - { - error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f"; - return token_type::parse_error; - } + switch (current) + { + // structural characters + case '[': + return token_type::begin_array; + case ']': + return token_type::end_array; + case '{': + return token_type::begin_object; + case '}': + return token_type::end_object; + case ':': + return token_type::name_separator; + case ',': + return token_type::value_separator; - case 0x0D: - { - error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r"; - return token_type::parse_error; - } + // literals + case 't': + { + std::array true_literal = {{static_cast('t'), static_cast('r'), static_cast('u'), static_cast('e')}}; + return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true); + } + case 'f': + { + std::array false_literal = {{static_cast('f'), static_cast('a'), static_cast('l'), static_cast('s'), static_cast('e')}}; + return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false); + } + case 'n': + { + std::array null_literal = {{static_cast('n'), static_cast('u'), static_cast('l'), static_cast('l')}}; + return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null); + } - case 0x0E: - { - error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E"; - return token_type::parse_error; - } + // string + case '\"': + return scan_string(); - case 0x0F: - { - error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F"; - return token_type::parse_error; - } + // number + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return scan_number(); - case 0x10: - { - error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010"; - return token_type::parse_error; - } + // end of input (the null byte is needed when parsing from + // string literals) + case '\0': + case char_traits::eof(): + return token_type::end_of_input; - case 0x11: - { - error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011"; - return token_type::parse_error; - } + // error + default: + error_message = "invalid literal"; + return token_type::parse_error; + } + } - case 0x12: - { - error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012"; - return token_type::parse_error; - } + private: + /// input adapter + InputAdapterType ia; - case 0x13: - { - error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013"; - return token_type::parse_error; - } + /// whether comments should be ignored (true) or signaled as errors (false) + const bool ignore_comments = false; - case 0x14: - { - error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014"; - return token_type::parse_error; - } + /// the current character + char_int_type current = char_traits::eof(); - case 0x15: - { - error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015"; - return token_type::parse_error; - } + /// whether the next get() call should just return current + bool next_unget = false; - case 0x16: - { - error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016"; - return token_type::parse_error; - } + /// the start position of the current token + position_t position {}; - case 0x17: - { - error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017"; - return token_type::parse_error; - } + /// raw input token string (for error messages) + std::vector token_string {}; - case 0x18: - { - error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018"; - return token_type::parse_error; - } + /// buffer for variable-length tokens (numbers, strings) + string_t token_buffer {}; - case 0x19: - { - error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019"; - return token_type::parse_error; - } + /// a description of occurred lexer errors + const char* error_message = ""; - case 0x1A: - { - error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A"; - return token_type::parse_error; - } + // number values + number_integer_t value_integer = 0; + number_unsigned_t value_unsigned = 0; + number_float_t value_float = 0; - case 0x1B: - { - error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B"; - return token_type::parse_error; - } + /// the decimal point + const char_int_type decimal_point_char = '.'; + /// the position of the decimal point in the input + std::size_t decimal_point_position = std::string::npos; +}; - case 0x1C: - { - error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C"; - return token_type::parse_error; - } +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END - case 0x1D: - { - error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D"; - return token_type::parse_error; - } +// #include - case 0x1E: - { - error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E"; - return token_type::parse_error; - } +// #include - case 0x1F: - { - error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F"; - return token_type::parse_error; - } +NLOHMANN_JSON_NAMESPACE_BEGIN - // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace)) - case 0x20: - case 0x21: - case 0x23: - case 0x24: - case 0x25: - case 0x26: - case 0x27: - case 0x28: - case 0x29: - case 0x2A: - case 0x2B: - case 0x2C: - case 0x2D: - case 0x2E: - case 0x2F: - case 0x30: - case 0x31: - case 0x32: - case 0x33: - case 0x34: - case 0x35: - case 0x36: - case 0x37: - case 0x38: - case 0x39: - case 0x3A: - case 0x3B: - case 0x3C: - case 0x3D: - case 0x3E: - case 0x3F: - case 0x40: - case 0x41: - case 0x42: - case 0x43: - case 0x44: - case 0x45: - case 0x46: - case 0x47: - case 0x48: - case 0x49: - case 0x4A: - case 0x4B: - case 0x4C: - case 0x4D: - case 0x4E: - case 0x4F: - case 0x50: - case 0x51: - case 0x52: - case 0x53: - case 0x54: - case 0x55: - case 0x56: - case 0x57: - case 0x58: - case 0x59: - case 0x5A: - case 0x5B: - case 0x5D: - case 0x5E: - case 0x5F: - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - case 0x67: - case 0x68: - case 0x69: - case 0x6A: - case 0x6B: - case 0x6C: - case 0x6D: - case 0x6E: - case 0x6F: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x76: - case 0x77: - case 0x78: - case 0x79: - case 0x7A: - case 0x7B: - case 0x7C: - case 0x7D: - case 0x7E: - case 0x7F: - { - add(current); - break; - } +/*! +@brief SAX interface - // U+0080..U+07FF: bytes C2..DF 80..BF - case 0xC2: - case 0xC3: - case 0xC4: - case 0xC5: - case 0xC6: - case 0xC7: - case 0xC8: - case 0xC9: - case 0xCA: - case 0xCB: - case 0xCC: - case 0xCD: - case 0xCE: - case 0xCF: - case 0xD0: - case 0xD1: - case 0xD2: - case 0xD3: - case 0xD4: - case 0xD5: - case 0xD6: - case 0xD7: - case 0xD8: - case 0xD9: - case 0xDA: - case 0xDB: - case 0xDC: - case 0xDD: - case 0xDE: - case 0xDF: - { - if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF}))) - { - return token_type::parse_error; - } - break; - } +This class describes the SAX interface used by @ref nlohmann::json::sax_parse. +Each function is called in different situations while the input is parsed. The +boolean return value informs the parser whether to continue processing the +input. +*/ +template +struct json_sax +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + /*! + @brief a null value was read + @return whether parsing should proceed + */ + virtual bool null() = 0; - // U+0800..U+0FFF: bytes E0 A0..BF 80..BF - case 0xE0: - { - if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) - { - return token_type::parse_error; - } - break; - } + /*! + @brief a boolean value was read + @param[in] val boolean value + @return whether parsing should proceed + */ + virtual bool boolean(bool val) = 0; - // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF - // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF - case 0xE1: - case 0xE2: - case 0xE3: - case 0xE4: - case 0xE5: - case 0xE6: - case 0xE7: - case 0xE8: - case 0xE9: - case 0xEA: - case 0xEB: - case 0xEC: - case 0xEE: - case 0xEF: - { - if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) - { - return token_type::parse_error; - } - break; - } + /*! + @brief an integer number was read + @param[in] val integer value + @return whether parsing should proceed + */ + virtual bool number_integer(number_integer_t val) = 0; - // U+D000..U+D7FF: bytes ED 80..9F 80..BF - case 0xED: - { - if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) - { - return token_type::parse_error; - } - break; - } + /*! + @brief an unsigned integer number was read + @param[in] val unsigned integer value + @return whether parsing should proceed + */ + virtual bool number_unsigned(number_unsigned_t val) = 0; - // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF - case 0xF0: - { - if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) - { - return token_type::parse_error; - } - break; - } + /*! + @brief a floating-point number was read + @param[in] val floating-point value + @param[in] s raw token value + @return whether parsing should proceed + */ + virtual bool number_float(number_float_t val, const string_t& s) = 0; - // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF - case 0xF1: - case 0xF2: - case 0xF3: - { - if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) - { - return token_type::parse_error; - } - break; - } + /*! + @brief a string value was read + @param[in] val string value + @return whether parsing should proceed + @note It is safe to move the passed string value. + */ + virtual bool string(string_t& val) = 0; - // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF - case 0xF4: - { - if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) - { - return token_type::parse_error; - } - break; - } + /*! + @brief a binary value was read + @param[in] val binary value + @return whether parsing should proceed + @note It is safe to move the passed binary value. + */ + virtual bool binary(binary_t& val) = 0; - // remaining bytes (80..C1 and F5..FF) are ill-formed - default: - { - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; - } - } - } - } + /*! + @brief the beginning of an object was read + @param[in] elements number of object elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_object(std::size_t elements) = 0; /*! - * @brief scan a comment - * @return whether comment could be scanned successfully - */ - bool scan_comment() - { - switch (get()) - { - // single-line comments skip input until a newline or EOF is read - case '/': - { - while (true) - { - switch (get()) - { - case '\n': - case '\r': - case char_traits::eof(): - case '\0': - return true; + @brief an object key was read + @param[in] val object key + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool key(string_t& val) = 0; - default: - break; - } - } - } + /*! + @brief the end of an object was read + @return whether parsing should proceed + */ + virtual bool end_object() = 0; - // multi-line comments skip input until */ is read - case '*': - { - while (true) - { - switch (get()) - { - case char_traits::eof(): - case '\0': - { - error_message = "invalid comment; missing closing '*/'"; - return false; - } + /*! + @brief the beginning of an array was read + @param[in] elements number of array elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_array(std::size_t elements) = 0; - case '*': - { - switch (get()) - { - case '/': - return true; + /*! + @brief the end of an array was read + @return whether parsing should proceed + */ + virtual bool end_array() = 0; + + /*! + @brief a parse error occurred + @param[in] position the position in the input where the error occurs + @param[in] last_token the last read token + @param[in] ex an exception object describing the error + @return whether parsing should proceed (must return false) + */ + virtual bool parse_error(std::size_t position, + const std::string& last_token, + const detail::exception& ex) = 0; + + json_sax() = default; + json_sax(const json_sax&) = default; + json_sax(json_sax&&) noexcept = default; + json_sax& operator=(const json_sax&) = default; + json_sax& operator=(json_sax&&) noexcept = default; + virtual ~json_sax() = default; +}; + +namespace detail +{ +constexpr std::size_t unknown_size() +{ + return (std::numeric_limits::max)(); +} + +/*! +@brief SAX implementation to create a JSON value from SAX events + +This class implements the @ref json_sax interface and processes the SAX events +to create a JSON value which makes it basically a DOM parser. The structure or +hierarchy of the JSON value is managed by the stack `ref_stack` which contains +a pointer to the respective array or object for each recursion depth. + +After successful parsing, the value that is passed by reference to the +constructor contains the parsed value. + +@tparam BasicJsonType the JSON type +*/ +template +class json_sax_dom_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using lexer_t = lexer; + + /*! + @param[in,out] r reference to a JSON value that is manipulated while + parsing + @param[in] allow_exceptions_ whether parse errors yield exceptions + */ + explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true, lexer_t* lexer_ = nullptr) + : root(r), allow_exceptions(allow_exceptions_), m_lexer_ref(lexer_) + {} + + // make class move-only + json_sax_dom_parser(const json_sax_dom_parser&) = delete; + json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete; + json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~json_sax_dom_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } - default: - { - unget(); - continue; - } - } - } + bool boolean(bool val) + { + handle_value(val); + return true; + } - default: - continue; - } - } - } + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } - // unexpected character after reading '/' - default: - { - error_message = "invalid comment; expecting '/' or '*' after '/'"; - return false; - } - } + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; } - JSON_HEDLEY_NON_NULL(2) - static void strtof(float& f, const char* str, char** endptr) noexcept + bool number_float(number_float_t val, const string_t& /*unused*/) { - f = std::strtof(str, endptr); + handle_value(val); + return true; } - JSON_HEDLEY_NON_NULL(2) - static void strtof(double& f, const char* str, char** endptr) noexcept + bool string(string_t& val) { - f = std::strtod(str, endptr); + handle_value(val); + return true; } - JSON_HEDLEY_NON_NULL(2) - static void strtof(long double& f, const char* str, char** endptr) noexcept + bool binary(binary_t& val) { - f = std::strtold(str, endptr); + handle_value(std::move(val)); + return true; } - /*! - @brief scan a number literal + bool start_object(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); - This function scans a string according to Sect. 6 of RFC 8259. +#if JSON_DIAGNOSTIC_POSITIONS + // Manually set the start position of the object here. + // Ensure this is after the call to handle_value to ensure correct start position. + if (m_lexer_ref) + { + // Lexer has read the first character of the object, so + // subtract 1 from the position to get the correct start position. + ref_stack.back()->start_position = m_lexer_ref->get_position() - 1; + } +#endif - The function is realized with a deterministic finite state machine derived - from the grammar described in RFC 8259. Starting in state "init", the - input is read and used to determined the next state. Only state "done" - accepts the number. State "error" is a trap state to model errors. In the - table below, "anything" means any character but the ones listed before. + if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back())); + } - state | 0 | 1-9 | e E | + | - | . | anything - ---------|----------|----------|----------|---------|---------|----------|----------- - init | zero | any1 | [error] | [error] | minus | [error] | [error] - minus | zero | any1 | [error] | [error] | [error] | [error] | [error] - zero | done | done | exponent | done | done | decimal1 | done - any1 | any1 | any1 | exponent | done | done | decimal1 | done - decimal1 | decimal2 | decimal2 | [error] | [error] | [error] | [error] | [error] - decimal2 | decimal2 | decimal2 | exponent | done | done | done | done - exponent | any2 | any2 | [error] | sign | sign | [error] | [error] - sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] - any2 | any2 | any2 | done | done | done | done | done + return true; + } - The state machine is realized with one label per state (prefixed with - "scan_number_") and `goto` statements between them. The state machine - contains cycles, but any cycle can be left when EOF is read. Therefore, - the function is guaranteed to terminate. + bool key(string_t& val) + { + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(ref_stack.back()->is_object()); - During scanning, the read bytes are stored in token_buffer. This string is - then converted to a signed integer, an unsigned integer, or a - floating-point number. + // add null at the given key and store the reference for later + object_element = &(ref_stack.back()->m_data.m_value.object->operator[](val)); + return true; + } - @return token_type::value_unsigned, token_type::value_integer, or - token_type::value_float if number could be successfully scanned, - token_type::parse_error otherwise + bool end_object() + { + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(ref_stack.back()->is_object()); - @note The scanner is independent of the current locale. Internally, the - locale's decimal point is used instead of `.` to work with the - locale-dependent converters. - */ - token_type scan_number() // lgtm [cpp/use-of-goto] +#if JSON_DIAGNOSTIC_POSITIONS + if (m_lexer_ref) + { + // Lexer's position is past the closing brace, so set that as the end position. + ref_stack.back()->end_position = m_lexer_ref->get_position(); + } +#endif + + ref_stack.back()->set_parents(); + ref_stack.pop_back(); + return true; + } + + bool start_array(std::size_t len) { - // reset token_buffer to store the number's bytes - reset(); + ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); - // the type of the parsed number; initially set to unsigned; will be - // changed if minus sign, decimal point or exponent is read - token_type number_type = token_type::value_unsigned; +#if JSON_DIAGNOSTIC_POSITIONS + // Manually set the start position of the array here. + // Ensure this is after the call to handle_value to ensure correct start position. + if (m_lexer_ref) + { + ref_stack.back()->start_position = m_lexer_ref->get_position() - 1; + } +#endif - // state (init): we just found out we need to scan a number - switch (current) + if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size())) { - case '-': - { - add(current); - goto scan_number_minus; - } + JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back())); + } - case '0': - { - add(current); - goto scan_number_zero; - } + return true; + } - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_any1; - } + bool end_array() + { + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(ref_stack.back()->is_array()); - // all other characters are rejected outside scan_number() - default: // LCOV_EXCL_LINE - JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE +#if JSON_DIAGNOSTIC_POSITIONS + if (m_lexer_ref) + { + // Lexer's position is past the closing bracket, so set that as the end position. + ref_stack.back()->end_position = m_lexer_ref->get_position(); } +#endif -scan_number_minus: - // state: we just parsed a leading minus sign - number_type = token_type::value_integer; - switch (get()) + ref_stack.back()->set_parents(); + ref_stack.pop_back(); + return true; + } + + template + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const Exception& ex) + { + errored = true; + static_cast(ex); + if (allow_exceptions) { - case '0': - { - add(current); - goto scan_number_zero; - } + JSON_THROW(ex); + } + return false; + } - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': + constexpr bool is_errored() const + { + return errored; + } + + private: + +#if JSON_DIAGNOSTIC_POSITIONS + void handle_diagnostic_positions_for_json_value(BasicJsonType& v) + { + if (m_lexer_ref) + { + // Lexer has read past the current field value, so set the end position to the current position. + // The start position will be set below based on the length of the string representation + // of the value. + v.end_position = m_lexer_ref->get_position(); + + switch (v.type()) { - add(current); - goto scan_number_any1; + case value_t::boolean: + { + // 4 and 5 are the string length of "true" and "false" + v.start_position = v.end_position - (v.m_data.m_value.boolean ? 4 : 5); + break; + } + + case value_t::null: + { + // 4 is the string length of "null" + v.start_position = v.end_position - 4; + break; + } + + case value_t::string: + { + // include the length of the quotes, which is 2 + v.start_position = v.end_position - v.m_data.m_value.string->size() - 2; + break; + } + + // As we handle the start and end positions for values created during parsing, + // we do not expect the following value type to be called. Regardless, set the positions + // in case this is created manually or through a different constructor. Exclude from lcov + // since the exact condition of this switch is esoteric. + // LCOV_EXCL_START + case value_t::discarded: + { + v.end_position = std::string::npos; + v.start_position = v.end_position; + break; + } + // LCOV_EXCL_STOP + case value_t::binary: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + { + v.start_position = v.end_position - m_lexer_ref->get_string().size(); + break; + } + case value_t::object: + case value_t::array: + { + // object and array are handled in start_object() and start_array() handlers + // skip setting the values here. + break; + } + default: // LCOV_EXCL_LINE + // Handle all possible types discretely, default handler should never be reached. + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert,-warnings-as-errors) LCOV_EXCL_LINE } + } + } +#endif + + /*! + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + */ + template + JSON_HEDLEY_RETURNS_NON_NULL + BasicJsonType* handle_value(Value&& v) + { + if (ref_stack.empty()) + { + root = BasicJsonType(std::forward(v)); - default: - { - error_message = "invalid number; expected digit after '-'"; - return token_type::parse_error; - } +#if JSON_DIAGNOSTIC_POSITIONS + handle_diagnostic_positions_for_json_value(root); +#endif + + return &root; } -scan_number_zero: - // state: we just parse a zero (maybe with a leading minus sign) - switch (get()) + JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); + + if (ref_stack.back()->is_array()) { - case '.': - { - add(decimal_point_char); - goto scan_number_decimal1; - } + ref_stack.back()->m_data.m_value.array->emplace_back(std::forward(v)); - case 'e': - case 'E': - { - add(current); - goto scan_number_exponent; - } +#if JSON_DIAGNOSTIC_POSITIONS + handle_diagnostic_positions_for_json_value(ref_stack.back()->m_data.m_value.array->back()); +#endif - default: - goto scan_number_done; + return &(ref_stack.back()->m_data.m_value.array->back()); } -scan_number_any1: - // state: we just parsed a number 0-9 (maybe with a leading minus sign) - switch (get()) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_any1; - } + JSON_ASSERT(ref_stack.back()->is_object()); + JSON_ASSERT(object_element); + *object_element = BasicJsonType(std::forward(v)); - case '.': - { - add(decimal_point_char); - goto scan_number_decimal1; - } +#if JSON_DIAGNOSTIC_POSITIONS + handle_diagnostic_positions_for_json_value(*object_element); +#endif - case 'e': - case 'E': - { - add(current); - goto scan_number_exponent; - } + return object_element; + } - default: - goto scan_number_done; - } + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; + /// the lexer reference to obtain the current position + lexer_t* m_lexer_ref = nullptr; +}; -scan_number_decimal1: - // state: we just parsed a decimal point - number_type = token_type::value_float; - switch (get()) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_decimal2; - } +template +class json_sax_dom_callback_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using parser_callback_t = typename BasicJsonType::parser_callback_t; + using parse_event_t = typename BasicJsonType::parse_event_t; + using lexer_t = lexer; - default: - { - error_message = "invalid number; expected digit after '.'"; - return token_type::parse_error; - } - } + json_sax_dom_callback_parser(BasicJsonType& r, + parser_callback_t cb, + const bool allow_exceptions_ = true, + lexer_t* lexer_ = nullptr) + : root(r), callback(std::move(cb)), allow_exceptions(allow_exceptions_), m_lexer_ref(lexer_) + { + keep_stack.push_back(true); + } -scan_number_decimal2: - // we just parsed at least one number after a decimal point - switch (get()) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_decimal2; - } + // make class move-only + json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~json_sax_dom_callback_parser() = default; - case 'e': - case 'E': - { - add(current); - goto scan_number_exponent; - } + bool null() + { + handle_value(nullptr); + return true; + } - default: - goto scan_number_done; - } + bool boolean(bool val) + { + handle_value(val); + return true; + } -scan_number_exponent: - // we just parsed an exponent - number_type = token_type::value_float; - switch (get()) - { - case '+': - case '-': - { - add(current); - goto scan_number_sign; - } + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_any2; - } + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } - default: - { - error_message = - "invalid number; expected '+', '-', or digit after exponent"; - return token_type::parse_error; - } - } + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } -scan_number_sign: - // we just parsed an exponent sign - switch (get()) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_any2; - } + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool binary(binary_t& val) + { + handle_value(std::move(val)); + return true; + } + + bool start_object(std::size_t len) + { + // check callback for object start + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::object_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::object, true); + ref_stack.push_back(val.second); + + if (ref_stack.back()) + { - default: +#if JSON_DIAGNOSTIC_POSITIONS + // Manually set the start position of the object here. + // Ensure this is after the call to handle_value to ensure correct start position. + if (m_lexer_ref) { - error_message = "invalid number; expected digit after exponent sign"; - return token_type::parse_error; + // Lexer has read the first character of the object, so + // subtract 1 from the position to get the correct start position. + ref_stack.back()->start_position = m_lexer_ref->get_position() - 1; } - } +#endif -scan_number_any2: - // we just parsed a number after the exponent or exponent sign - switch (get()) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': + // check object limit + if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size())) { - add(current); - goto scan_number_any2; + JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back())); } - - default: - goto scan_number_done; } + return true; + } -scan_number_done: - // unget the character after the number (we only read it to know that - // we are done scanning a number) - unget(); + bool key(string_t& val) + { + BasicJsonType k = BasicJsonType(val); - char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) - errno = 0; + // check callback for the key + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::key, k); + key_keep_stack.push_back(keep); - // try to parse integers first and fall back to floats - if (number_type == token_type::value_unsigned) + // add discarded value at the given key and store the reference for later + if (keep && ref_stack.back()) { - const auto x = std::strtoull(token_buffer.data(), &endptr, 10); + object_element = &(ref_stack.back()->m_data.m_value.object->operator[](val) = discarded); + } - // we checked the number format before - JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + return true; + } - if (errno == 0) - { - value_unsigned = static_cast(x); - if (value_unsigned == x) - { - return token_type::value_unsigned; - } - } - } - else if (number_type == token_type::value_integer) + bool end_object() + { + if (ref_stack.back()) { - const auto x = std::strtoll(token_buffer.data(), &endptr, 10); - - // we checked the number format before - JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + if (!callback(static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) + { + // discard object + *ref_stack.back() = discarded; - if (errno == 0) +#if JSON_DIAGNOSTIC_POSITIONS + // Set start/end positions for discarded object. + handle_diagnostic_positions_for_json_value(*ref_stack.back()); +#endif + } + else { - value_integer = static_cast(x); - if (value_integer == x) + +#if JSON_DIAGNOSTIC_POSITIONS + if (m_lexer_ref) { - return token_type::value_integer; + // Lexer's position is past the closing brace, so set that as the end position. + ref_stack.back()->end_position = m_lexer_ref->get_position(); } +#endif + + ref_stack.back()->set_parents(); } } - // this code is reached if we parse a floating-point number or if an - // integer conversion above failed - strtof(value_float, token_buffer.data(), &endptr); - - // we checked the number format before - JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); - - return token_type::value_float; - } + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(!keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); - /*! - @param[in] literal_text the literal text to expect - @param[in] length the length of the passed literal text - @param[in] return_type the token type to return on success - */ - JSON_HEDLEY_NON_NULL(2) - token_type scan_literal(const char_type* literal_text, const std::size_t length, - token_type return_type) - { - JSON_ASSERT(char_traits::to_char_type(current) == literal_text[0]); - for (std::size_t i = 1; i < length; ++i) + if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured()) { - if (JSON_HEDLEY_UNLIKELY(char_traits::to_char_type(get()) != literal_text[i])) + // remove discarded value + for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it) { - error_message = "invalid literal"; - return token_type::parse_error; + if (it->is_discarded()) + { + ref_stack.back()->erase(it); + break; + } } } - return return_type; - } - - ///////////////////// - // input management - ///////////////////// - /// reset token_buffer; current character is beginning of token - void reset() noexcept - { - token_buffer.clear(); - token_string.clear(); - token_string.push_back(char_traits::to_char_type(current)); + return true; } - /* - @brief get next character from the input - - This function provides the interface to the used input adapter. It does - not throw in case the input reached EOF, but returns a - `char_traits::eof()` in that case. Stores the scanned characters - for use in error messages. - - @return character read from the input - */ - char_int_type get() + bool start_array(std::size_t len) { - ++position.chars_read_total; - ++position.chars_read_current_line; + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::array_start, discarded); + keep_stack.push_back(keep); - if (next_unget) - { - // just reset the next_unget variable and work with current - next_unget = false; - } - else - { - current = ia.get_character(); - } + auto val = handle_value(BasicJsonType::value_t::array, true); + ref_stack.push_back(val.second); - if (JSON_HEDLEY_LIKELY(current != char_traits::eof())) + if (ref_stack.back()) { - token_string.push_back(char_traits::to_char_type(current)); - } - if (current == '\n') - { - ++position.lines_read; - position.chars_read_current_line = 0; +#if JSON_DIAGNOSTIC_POSITIONS + // Manually set the start position of the array here. + // Ensure this is after the call to handle_value to ensure correct start position. + if (m_lexer_ref) + { + // Lexer has read the first character of the array, so + // subtract 1 from the position to get the correct start position. + ref_stack.back()->start_position = m_lexer_ref->get_position() - 1; + } +#endif + + // check array limit + if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back())); + } } - return current; + return true; } - /*! - @brief unget current character (read it again on next get) - - We implement unget by setting variable next_unget to true. The input is not - changed - we just simulate ungetting by modifying chars_read_total, - chars_read_current_line, and token_string. The next call to get() will - behave as if the unget character is read again. - */ - void unget() + bool end_array() { - next_unget = true; - - --position.chars_read_total; + bool keep = true; - // in case we "unget" a newline, we have to also decrement the lines_read - if (position.chars_read_current_line == 0) + if (ref_stack.back()) { - if (position.lines_read > 0) + keep = callback(static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); + if (keep) { - --position.lines_read; - } - } - else - { - --position.chars_read_current_line; - } - if (JSON_HEDLEY_LIKELY(current != char_traits::eof())) - { - JSON_ASSERT(!token_string.empty()); - token_string.pop_back(); - } - } +#if JSON_DIAGNOSTIC_POSITIONS + if (m_lexer_ref) + { + // Lexer's position is past the closing bracket, so set that as the end position. + ref_stack.back()->end_position = m_lexer_ref->get_position(); + } +#endif - /// add a character to token_buffer - void add(char_int_type c) - { - token_buffer.push_back(static_cast(c)); - } + ref_stack.back()->set_parents(); + } + else + { + // discard array + *ref_stack.back() = discarded; - public: - ///////////////////// - // value getters - ///////////////////// +#if JSON_DIAGNOSTIC_POSITIONS + // Set start/end positions for discarded array. + handle_diagnostic_positions_for_json_value(*ref_stack.back()); +#endif + } + } - /// return integer value - constexpr number_integer_t get_number_integer() const noexcept - { - return value_integer; - } + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(!keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); - /// return unsigned integer value - constexpr number_unsigned_t get_number_unsigned() const noexcept - { - return value_unsigned; - } + // remove discarded value + if (!keep && !ref_stack.empty() && ref_stack.back()->is_array()) + { + ref_stack.back()->m_data.m_value.array->pop_back(); + } - /// return floating-point value - constexpr number_float_t get_number_float() const noexcept - { - return value_float; + return true; } - /// return current string value (implicitly resets the token; useful only once) - string_t& get_string() + template + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const Exception& ex) { - return token_buffer; + errored = true; + static_cast(ex); + if (allow_exceptions) + { + JSON_THROW(ex); + } + return false; } - ///////////////////// - // diagnostics - ///////////////////// - - /// return position of last read token - constexpr position_t get_position() const noexcept + constexpr bool is_errored() const { - return position; + return errored; } - /// return the last read token (for errors only). Will never contain EOF - /// (an arbitrary value that is not a valid char value, often -1), because - /// 255 may legitimately occur. May contain NUL, which should be escaped. - std::string get_token_string() const + private: + +#if JSON_DIAGNOSTIC_POSITIONS + void handle_diagnostic_positions_for_json_value(BasicJsonType& v) { - // escape control characters - std::string result; - for (const auto c : token_string) + if (m_lexer_ref) { - if (static_cast(c) <= '\x1F') - { - // escape control characters - std::array cs{{}}; - static_cast((std::snprintf)(cs.data(), cs.size(), "", static_cast(c))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) - result += cs.data(); - } - else + // Lexer has read past the current field value, so set the end position to the current position. + // The start position will be set below based on the length of the string representation + // of the value. + v.end_position = m_lexer_ref->get_position(); + + switch (v.type()) { - // add character as is - result.push_back(static_cast(c)); + case value_t::boolean: + { + // 4 and 5 are the string length of "true" and "false" + v.start_position = v.end_position - (v.m_data.m_value.boolean ? 4 : 5); + break; + } + + case value_t::null: + { + // 4 is the string length of "null" + v.start_position = v.end_position - 4; + break; + } + + case value_t::string: + { + // include the length of the quotes, which is 2 + v.start_position = v.end_position - v.m_data.m_value.string->size() - 2; + break; + } + + case value_t::discarded: + { + v.end_position = std::string::npos; + v.start_position = v.end_position; + break; + } + + case value_t::binary: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + { + v.start_position = v.end_position - m_lexer_ref->get_string().size(); + break; + } + + case value_t::object: + case value_t::array: + { + // object and array are handled in start_object() and start_array() handlers + // skip setting the values here. + break; + } + default: // LCOV_EXCL_LINE + // Handle all possible types discretely, default handler should never be reached. + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert,-warnings-as-errors) LCOV_EXCL_LINE } } - - return result; } +#endif - /// return syntax error message - JSON_HEDLEY_RETURNS_NON_NULL - constexpr const char* get_error_message() const noexcept - { - return error_message; - } + /*! + @param[in] v value to add to the JSON value we build during parsing + @param[in] skip_callback whether we should skip calling the callback + function; this is required after start_array() and + start_object() SAX events, because otherwise we would call the + callback function with an empty array or object, respectively. - ///////////////////// - // actual scanner - ///////////////////// + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements - /*! - @brief skip the UTF-8 byte order mark - @return true iff there is no BOM or the correct BOM has been skipped + @return pair of boolean (whether value should be kept) and pointer (to the + passed value in the ref_stack hierarchy; nullptr if not kept) */ - bool skip_bom() + template + std::pair handle_value(Value&& v, const bool skip_callback = false) { - if (get() == 0xEF) + JSON_ASSERT(!keep_stack.empty()); + + // do not handle this value if we know it would be added to a discarded + // container + if (!keep_stack.back()) { - // check if we completely parse the BOM - return get() == 0xBB && get() == 0xBF; + return {false, nullptr}; } - // the first character is not the beginning of the BOM; unget it to - // process is later - unget(); - return true; - } + // create value + auto value = BasicJsonType(std::forward(v)); - void skip_whitespace() - { - do +#if JSON_DIAGNOSTIC_POSITIONS + handle_diagnostic_positions_for_json_value(value); +#endif + + // check callback + const bool keep = skip_callback || callback(static_cast(ref_stack.size()), parse_event_t::value, value); + + // do not handle this value if we just learnt it shall be discarded + if (!keep) { - get(); + return {false, nullptr}; } - while (current == ' ' || current == '\t' || current == '\n' || current == '\r'); - } - token_type scan() - { - // initially, skip the BOM - if (position.chars_read_total == 0 && !skip_bom()) + if (ref_stack.empty()) { - error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given"; - return token_type::parse_error; + root = std::move(value); + return {true, & root}; } - // read next character and ignore whitespace - skip_whitespace(); - - // ignore comments - while (ignore_comments && current == '/') + // skip this value if we already decided to skip the parent + // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360) + if (!ref_stack.back()) { - if (!scan_comment()) - { - return token_type::parse_error; - } + return {false, nullptr}; + } - // skip following whitespace - skip_whitespace(); + // we now only expect arrays and objects + JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); + + // array + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_data.m_value.array->emplace_back(std::move(value)); + return {true, & (ref_stack.back()->m_data.m_value.array->back())}; } - switch (current) + // object + JSON_ASSERT(ref_stack.back()->is_object()); + // check if we should store an element for the current key + JSON_ASSERT(!key_keep_stack.empty()); + const bool store_element = key_keep_stack.back(); + key_keep_stack.pop_back(); + + if (!store_element) { - // structural characters - case '[': - return token_type::begin_array; - case ']': - return token_type::end_array; - case '{': - return token_type::begin_object; - case '}': - return token_type::end_object; - case ':': - return token_type::name_separator; - case ',': - return token_type::value_separator; + return {false, nullptr}; + } - // literals - case 't': - { - std::array true_literal = {{static_cast('t'), static_cast('r'), static_cast('u'), static_cast('e')}}; - return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true); - } - case 'f': - { - std::array false_literal = {{static_cast('f'), static_cast('a'), static_cast('l'), static_cast('s'), static_cast('e')}}; - return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false); - } - case 'n': - { - std::array null_literal = {{static_cast('n'), static_cast('u'), static_cast('l'), static_cast('l')}}; - return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null); - } + JSON_ASSERT(object_element); + *object_element = std::move(value); + return {true, object_element}; + } - // string - case '\"': - return scan_string(); + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// stack to manage which values to keep + std::vector keep_stack {}; // NOLINT(readability-redundant-member-init) + /// stack to manage which object keys to keep + std::vector key_keep_stack {}; // NOLINT(readability-redundant-member-init) + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// callback function + const parser_callback_t callback = nullptr; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; + /// a discarded value for the callback + BasicJsonType discarded = BasicJsonType::value_t::discarded; + /// the lexer reference to obtain the current position + lexer_t* m_lexer_ref = nullptr; +}; - // number - case '-': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - return scan_number(); +template +class json_sax_acceptor +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; - // end of input (the null byte is needed when parsing from - // string literals) - case '\0': - case char_traits::eof(): - return token_type::end_of_input; + bool null() + { + return true; + } + + bool boolean(bool /*unused*/) + { + return true; + } - // error - default: - error_message = "invalid literal"; - return token_type::parse_error; - } + bool number_integer(number_integer_t /*unused*/) + { + return true; } - private: - /// input adapter - InputAdapterType ia; + bool number_unsigned(number_unsigned_t /*unused*/) + { + return true; + } - /// whether comments should be ignored (true) or signaled as errors (false) - const bool ignore_comments = false; + bool number_float(number_float_t /*unused*/, const string_t& /*unused*/) + { + return true; + } - /// the current character - char_int_type current = char_traits::eof(); + bool string(string_t& /*unused*/) + { + return true; + } - /// whether the next get() call should just return current - bool next_unget = false; + bool binary(binary_t& /*unused*/) + { + return true; + } - /// the start position of the current token - position_t position {}; + bool start_object(std::size_t /*unused*/ = detail::unknown_size()) + { + return true; + } - /// raw input token string (for error messages) - std::vector token_string {}; + bool key(string_t& /*unused*/) + { + return true; + } - /// buffer for variable-length tokens (numbers, strings) - string_t token_buffer {}; + bool end_object() + { + return true; + } - /// a description of occurred lexer errors - const char* error_message = ""; + bool start_array(std::size_t /*unused*/ = detail::unknown_size()) + { + return true; + } - // number values - number_integer_t value_integer = 0; - number_unsigned_t value_unsigned = 0; - number_float_t value_float = 0; + bool end_array() + { + return true; + } - /// the decimal point - const char_int_type decimal_point_char = '.'; + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/) + { + return false; + } }; } // namespace detail NLOHMANN_JSON_NAMESPACE_END +// #include + // #include // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -9196,7 +9891,7 @@ enum class cbor_tag_handler_t @note from https://stackoverflow.com/a/1001328/266378 */ -static inline bool little_endianness(int num = 1) noexcept +inline bool little_endianness(int num = 1) noexcept { return *reinterpret_cast(&num) == 1; } @@ -9208,7 +9903,7 @@ static inline bool little_endianness(int num = 1) noexcept /*! @brief deserialization of CBOR, MessagePack, and UBJSON values */ -template> +template> class binary_reader { using number_integer_t = typename BasicJsonType::number_integer_t; @@ -9315,7 +10010,7 @@ class binary_reader std::int32_t document_size{}; get_number(input_format_t::bson, document_size); - if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast(-1)))) + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(detail::unknown_size()))) { return false; } @@ -9471,7 +10166,13 @@ class binary_reader return get_number(input_format_t::bson, value) && sax->number_integer(value); } - default: // anything else not supported (yet) + case 0x11: // uint64 + { + std::uint64_t value{}; + return get_number(input_format_t::bson, value) && sax->number_unsigned(value); + } + + default: // anything else is not supported (yet) { std::array cr{{}}; static_cast((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(element_type))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) @@ -9537,7 +10238,7 @@ class binary_reader std::int32_t document_size{}; get_number(input_format_t::bson, document_size); - if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast(-1)))) + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(detail::unknown_size()))) { return false; } @@ -9797,7 +10498,7 @@ class binary_reader } case 0x9F: // array (indefinite length) - return get_cbor_array(static_cast(-1), tag_handler); + return get_cbor_array(detail::unknown_size(), tag_handler); // map (0x00..0x17 pairs of data items follow) case 0xA0: @@ -9851,7 +10552,7 @@ class binary_reader } case 0xBF: // map (indefinite length) - return get_cbor_object(static_cast(-1), tag_handler); + return get_cbor_object(detail::unknown_size(), tag_handler); case 0xC6: // tagged item case 0xC7: @@ -9868,7 +10569,7 @@ class binary_reader case 0xD2: case 0xD3: case 0xD4: - case 0xD8: // tagged item (1 bytes follow) + case 0xD8: // tagged item (1 byte follows) case 0xD9: // tagged item (2 bytes follow) case 0xDA: // tagged item (4 bytes follow) case 0xDB: // tagged item (8 bytes follow) @@ -9920,7 +10621,7 @@ class binary_reader case cbor_tag_handler_t::store: { binary_t b; - // use binary subtype and store in binary container + // use binary subtype and store in a binary container switch (current) { case 0xD8: @@ -9989,7 +10690,7 @@ class binary_reader const auto byte1 = static_cast(byte1_raw); const auto byte2 = static_cast(byte2_raw); - // code from RFC 7049, Appendix D, Figure 3: + // Code from RFC 7049, Appendix D, Figure 3: // As half-precision floating-point numbers were only added // to IEEE 754 in 2008, today's programming platforms often // still only have limited support for them. It is very @@ -10239,7 +10940,7 @@ class binary_reader } /*! - @param[in] len the length of the array or static_cast(-1) for an + @param[in] len the length of the array or detail::unknown_size() for an array of indefinite size @param[in] tag_handler how CBOR tags should be treated @return whether array creation completed @@ -10252,7 +10953,7 @@ class binary_reader return false; } - if (len != static_cast(-1)) + if (len != detail::unknown_size()) { for (std::size_t i = 0; i < len; ++i) { @@ -10277,7 +10978,7 @@ class binary_reader } /*! - @param[in] len the length of the object or static_cast(-1) for an + @param[in] len the length of the object or detail::unknown_size() for an object of indefinite size @param[in] tag_handler how CBOR tags should be treated @return whether object creation completed @@ -10293,7 +10994,7 @@ class binary_reader if (len != 0) { string_t key; - if (len != static_cast(-1)) + if (len != detail::unknown_size()) { for (std::size_t i = 0; i < len; ++i) { @@ -11296,7 +11997,7 @@ class binary_reader { break; } - if (is_ndarray) // ndarray dimensional vector can only contain integers, and can not embed another array + if (is_ndarray) // ndarray dimensional vector can only contain integers and cannot embed another array { return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, exception_message(input_format, "ndarray dimensional vector is not allowed", "size"), nullptr)); } @@ -11329,8 +12030,16 @@ class binary_reader result = 1; for (auto i : dim) { + // Pre-multiplication overflow check: if i > 0 and result > SIZE_MAX/i, then result*i would overflow. + // This check must happen before multiplication since overflow detection after the fact is unreliable + // as modular arithmetic can produce any value, not just 0 or SIZE_MAX. + if (JSON_HEDLEY_UNLIKELY(i > 0 && result > (std::numeric_limits::max)() / i)) + { + return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr)); + } result *= i; - if (result == 0 || result == npos) // because dim elements shall not have zeros, result = 0 means overflow happened; it also can't be npos as it is used to initialize size in get_ubjson_size_type() + // Additional post-multiplication check to catch any edge cases the pre-check might miss + if (result == 0 || result == npos) { return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr)); } @@ -11456,6 +12165,16 @@ class binary_reader case 'Z': // null return sax->null(); + case 'B': // byte + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint8_t number{}; + return get_number(input_format, number) && sax->number_unsigned(number); + } + case 'U': { std::uint8_t number{}; @@ -11536,7 +12255,7 @@ class binary_reader const auto byte1 = static_cast(byte1_raw); const auto byte2 = static_cast(byte2_raw); - // code from RFC 7049, Appendix D, Figure 3: + // Code from RFC 7049, Appendix D, Figure 3: // As half-precision floating-point numbers were only added // to IEEE 754 in 2008, today's programming platforms often // still only have limited support for them. It is very @@ -11656,7 +12375,7 @@ class binary_reader return false; } - if (size_and_type.second == 'C') + if (size_and_type.second == 'C' || size_and_type.second == 'B') { size_and_type.second = 'U'; } @@ -11678,6 +12397,13 @@ class binary_reader return (sax->end_array() && sax->end_object()); } + // If BJData type marker is 'B' decode as binary + if (input_format == input_format_t::bjdata && size_and_type.first != npos && size_and_type.second == 'B') + { + binary_t result; + return get_binary(input_format, size_and_type.first, result) && sax->binary(result); + } + if (size_and_type.first != npos) { if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first))) @@ -11711,7 +12437,7 @@ class binary_reader } else { - if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast(-1)))) + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(detail::unknown_size()))) { return false; } @@ -11789,7 +12515,7 @@ class binary_reader } else { - if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast(-1)))) + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(detail::unknown_size()))) { return false; } @@ -11817,7 +12543,7 @@ class binary_reader bool get_ubjson_high_precision_number() { - // get size of following number string + // get the size of the following number string std::size_t size{}; bool no_ndarray = true; auto res = get_ubjson_size_value(size, no_ndarray); @@ -11900,6 +12626,29 @@ class binary_reader return current = ia.get_character(); } + /*! + @brief get_to read into a primitive type + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns false instead + + @return bool, whether the read was successful + */ + template + bool get_to(T& dest, const input_format_t format, const char* context) + { + auto new_chars_read = ia.get_elements(&dest); + chars_read += new_chars_read; + if (JSON_HEDLEY_UNLIKELY(new_chars_read < sizeof(T))) + { + // in case of failure, advance position by 1 to report the failing location + ++chars_read; + sax->parse_error(chars_read, "", parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), nullptr)); + return false; + } + return true; + } + /*! @return character read from the input after ignoring all 'N' entries */ @@ -11914,6 +12663,33 @@ class binary_reader return current; } + template + static void byte_swap(NumberType& number) + { + constexpr std::size_t sz = sizeof(number); +#ifdef __cpp_lib_byteswap + if constexpr (sz == 1) + { + return; + } + else if constexpr(std::is_integral_v) + { + number = std::byteswap(number); + return; + } + else + { +#endif + auto* ptr = reinterpret_cast(&number); + for (std::size_t i = 0; i < sz / 2; ++i) + { + std::swap(ptr[i], ptr[sz - i - 1]); + } +#ifdef __cpp_lib_byteswap + } +#endif + } + /* @brief read a number from the input @@ -11932,29 +12708,16 @@ class binary_reader template bool get_number(const input_format_t format, NumberType& result) { - // step 1: read input into array with system's byte order - std::array vec{}; - for (std::size_t i = 0; i < sizeof(NumberType); ++i) - { - get(); - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number"))) - { - return false; - } + // read in the original format - // reverse byte order prior to conversion if necessary - if (is_little_endian != (InputIsLittleEndian || format == input_format_t::bjdata)) - { - vec[sizeof(NumberType) - i - 1] = static_cast(current); - } - else - { - vec[i] = static_cast(current); // LCOV_EXCL_LINE - } + if (JSON_HEDLEY_UNLIKELY(!get_to(result, format, "number"))) + { + return false; + } + if (is_little_endian != (InputIsLittleEndian || format == input_format_t::bjdata)) + { + byte_swap(result); } - - // step 2: convert array into number of type T and return - std::memcpy(&result, vec.data(), sizeof(NumberType)); return true; } @@ -12019,7 +12782,7 @@ class binary_reader success = false; break; } - result.push_back(static_cast(current)); + result.push_back(static_cast(current)); } return success; } @@ -12093,7 +12856,7 @@ class binary_reader } private: - static JSON_INLINE_VARIABLE constexpr std::size_t npos = static_cast(-1); + static JSON_INLINE_VARIABLE constexpr std::size_t npos = detail::unknown_size(); /// input adapter InputAdapterType ia; @@ -12119,6 +12882,7 @@ class binary_reader #define JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_ \ make_array( \ + bjd_type{'B', "byte"}, \ bjd_type{'C', "char"}, \ bjd_type{'D', "double"}, \ bjd_type{'I', "int16"}, \ @@ -12161,10 +12925,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -12238,12 +13002,14 @@ class parser public: /// a parser reading from an input adapter explicit parser(InputAdapterType&& adapter, - const parser_callback_t cb = nullptr, + parser_callback_t cb = nullptr, const bool allow_exceptions_ = true, - const bool skip_comments = false) - : callback(cb) - , m_lexer(std::move(adapter), skip_comments) + const bool ignore_comments = false, + const bool ignore_trailing_commas_ = false) + : callback(std::move(cb)) + , m_lexer(std::move(adapter), ignore_comments) , allow_exceptions(allow_exceptions_) + , ignore_trailing_commas(ignore_trailing_commas_) { // read first token get_token(); @@ -12263,7 +13029,7 @@ class parser { if (callback) { - json_sax_dom_callback_parser sdp(result, callback, allow_exceptions); + json_sax_dom_callback_parser sdp(result, callback, allow_exceptions, &m_lexer); sax_parse_internal(&sdp); // in strict mode, input must be completely read @@ -12275,7 +13041,7 @@ class parser exception_message(token_type::end_of_input, "value"), nullptr)); } - // in case of an error, return discarded value + // in case of an error, return a discarded value if (sdp.is_errored()) { result = value_t::discarded; @@ -12291,7 +13057,7 @@ class parser } else { - json_sax_dom_parser sdp(result, allow_exceptions); + json_sax_dom_parser sdp(result, allow_exceptions, &m_lexer); sax_parse_internal(&sdp); // in strict mode, input must be completely read @@ -12302,7 +13068,7 @@ class parser parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr)); } - // in case of an error, return discarded value + // in case of an error, return a discarded value if (sdp.is_errored()) { result = value_t::discarded; @@ -12363,7 +13129,7 @@ class parser { case token_type::begin_object: { - if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast(-1)))) + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(detail::unknown_size()))) { return false; } @@ -12408,7 +13174,7 @@ class parser case token_type::begin_array: { - if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast(-1)))) + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(detail::unknown_size()))) { return false; } @@ -12505,7 +13271,7 @@ class parser case token_type::parse_error: { - // using "uninitialized" to avoid "expected" message + // using "uninitialized" to avoid an "expected" message return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr)); @@ -12553,11 +13319,17 @@ 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(); - continue; + + // 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 ] @@ -12586,32 +13358,39 @@ 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) { - // parse key - if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string)) - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr)); - } + get_token(); - if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) + // if ignore_trailing_commas and last_token is }, we can continue to "closing }" + if (!(ignore_trailing_commas && last_token == token_type::end_object)) { - return false; - } + // parse key + if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr)); + } - // parse separator (:) - if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr)); - } + if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) + { + return false; + } - // parse values - get_token(); - continue; + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr)); + } + + // parse values + get_token(); + continue; + } } // closing } @@ -12682,6 +13461,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 @@ -12690,10 +13471,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -12703,10 +13484,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -12725,9 +13506,9 @@ namespace detail @brief an iterator for primitive JSON types This class models an iterator for primitive JSON types (boolean, number, -string). It's only purpose is to allow the iterator/const_iterator classes +string). Its only purpose is to allow the iterator/const_iterator classes to "iterate" over primitive values. Internally, the iterator is modeled by -a `difference_type` variable. Value begin_value (`0`) models the begin, +a `difference_type` variable. Value begin_value (`0`) models the begin and end_value (`1`) models past the end. */ class primitive_iterator_t @@ -12862,10 +13643,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -12892,7 +13673,7 @@ NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { -// forward declare, to be able to friend it later on +// forward declare to be able to friend it later on template class iteration_proxy; template class iteration_proxy_value; @@ -13332,7 +14113,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci /*! @brief comparison: equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. + @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized. */ template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > bool operator==(const IterImpl& other) const @@ -13343,7 +14124,11 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object)); } - JSON_ASSERT(m_object != nullptr); + // value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493 + if (m_object == nullptr) + { + return true; + } switch (m_object->m_data.m_type) { @@ -13368,7 +14153,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci /*! @brief comparison: not equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. + @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized. */ template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > bool operator!=(const IterImpl& other) const @@ -13378,7 +14163,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci /*! @brief comparison: smaller - @pre The iterator is initialized; i.e. `m_object != nullptr`. + @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized. */ bool operator<(const iter_impl& other) const { @@ -13388,7 +14173,12 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object)); } - JSON_ASSERT(m_object != nullptr); + // value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493 + if (m_object == nullptr) + { + // the iterators are both value-initialized and are to be considered equal, but this function checks for smaller, so we return false + return false; + } switch (m_object->m_data.m_type) { @@ -13413,7 +14203,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci /*! @brief comparison: less than or equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. + @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized. */ bool operator<=(const iter_impl& other) const { @@ -13422,7 +14212,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci /*! @brief comparison: greater than - @pre The iterator is initialized; i.e. `m_object != nullptr`. + @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized. */ bool operator>(const iter_impl& other) const { @@ -13431,7 +14221,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci /*! @brief comparison: greater than or equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. + @pre (1) The iterator is initialized; i.e. `m_object != nullptr`, or (2) both iterators are value-initialized. */ bool operator>=(const iter_impl& other) const { @@ -13624,10 +14414,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -13759,10 +14549,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -13801,10 +14591,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -14034,7 +14824,7 @@ class json_pointer } const char* p = s.c_str(); - char* p_end = nullptr; + char* p_end = nullptr; // NOLINT(misc-const-correctness) errno = 0; // strtoull doesn't reset errno const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int) if (p == p_end // invalid input or empty string @@ -14091,7 +14881,7 @@ class json_pointer { if (reference_token == "0") { - // start a new array if reference token is 0 + // start a new array if the reference token is 0 result = &result->operator[](0); } else @@ -14120,7 +14910,7 @@ class json_pointer The following code is only reached if there exists a reference token _and_ the current value is primitive. In this case, we have an error situation, because primitive values may only occur as - single value; that is, with an empty list of reference tokens. + a single value; that is, with an empty list of reference tokens. */ case detail::value_t::string: case detail::value_t::boolean: @@ -14164,7 +14954,7 @@ class json_pointer // convert null values to arrays or objects before continuing if (ptr->is_null()) { - // check if reference token is a number + // check if the reference token is a number const bool nums = std::all_of(reference_token.begin(), reference_token.end(), [](const unsigned char x) @@ -14172,7 +14962,7 @@ class json_pointer return std::isdigit(x); }); - // change value to array for numbers or "-" or to object otherwise + // change value to an array for numbers or "-" or to object otherwise *ptr = (nums || reference_token == "-") ? detail::value_t::array : detail::value_t::object; @@ -14415,7 +15205,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9'))) { - // first char should be between '1' and '9' + // the first char should be between '1' and '9' return false; } for (std::size_t i = 1; i < reference_token.size(); i++) @@ -14479,7 +15269,7 @@ class json_pointer return result; } - // check if nonempty reference string begins with slash + // check if a nonempty reference string begins with slash if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) { JSON_THROW(detail::parse_error::create(107, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr)); @@ -14553,10 +15343,10 @@ class json_pointer } else { - // iterate array and use index as reference string + // iterate array and use index as a reference string for (std::size_t i = 0; i < value.m_data.m_value.array->size(); ++i) { - flatten(detail::concat(reference_string, '/', std::to_string(i)), + flatten(detail::concat(reference_string, '/', std::to_string(i)), value.m_data.m_value.array->operator[](i), result); } } @@ -14575,7 +15365,7 @@ class json_pointer // iterate object and use keys as reference string for (const auto& element : *value.m_data.m_value.object) { - flatten(detail::concat(reference_string, '/', detail::escape(element.first)), element.second, result); + flatten(detail::concat(reference_string, '/', detail::escape(element.first)), element.second, result); } } break; @@ -14591,7 +15381,7 @@ class json_pointer case detail::value_t::discarded: default: { - // add primitive value with its reference string + // add a primitive value with its reference string result[reference_string] = value; break; } @@ -14627,17 +15417,17 @@ class json_pointer JSON_THROW(detail::type_error::create(315, "values in object must be primitive", &element.second)); } - // assign value to reference pointed to by JSON pointer; Note that if - // the JSON pointer is "" (i.e., points to the whole value), function - // get_and_create returns a reference to result itself. An assignment - // will then create a primitive value. + // Assign the value to the reference pointed to by JSON pointer. Note + // that if the JSON pointer is "" (i.e., points to the whole value), + // function get_and_create returns a reference to the result itself. + // An assignment will then create a primitive value. json_pointer(element.first).get_and_create(result) = element.second; } return result; } - // can't use conversion operator because of ambiguity + // can't use the conversion operator because of ambiguity json_pointer convert() const& { json_pointer result; @@ -14732,7 +15522,7 @@ class json_pointer }; #if !JSON_HAS_THREE_WAY_COMPARISON -// functions cannot be defined inside class due to ODR violations +// functions cannot be defined inside the class due to ODR violations template inline bool operator==(const json_pointer& lhs, const json_pointer& rhs) noexcept @@ -14796,10 +15586,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -14881,6 +15671,8 @@ NLOHMANN_JSON_NAMESPACE_END // #include +// #include + // #include // #include @@ -14888,10 +15680,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -14914,10 +15706,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -15068,6 +15860,13 @@ NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { +/// how to encode BJData +enum class bjdata_version_t +{ + draft2, + draft3, +}; + /////////////////// // binary writer // /////////////////// @@ -15652,7 +16451,7 @@ class binary_writer case value_t::binary: { // step 0: determine if the binary type has a set subtype to - // determine whether or not to use the ext or fixext types + // determine whether to use the ext or fixext types const bool use_ext = j.m_data.m_value.binary->has_subtype(); // step 1: write control byte and the byte string length @@ -15775,11 +16574,14 @@ class binary_writer @param[in] use_type whether to use '$' prefixes (optimized format) @param[in] add_prefix whether prefixes need to be used for this value @param[in] use_bjdata whether write in BJData format, default is false + @param[in] bjdata_version which BJData version to use, default is draft2 */ void write_ubjson(const BasicJsonType& j, const bool use_count, const bool use_type, const bool add_prefix = true, - const bool use_bjdata = false) + const bool use_bjdata = false, const bjdata_version_t bjdata_version = bjdata_version_t::draft2) { + const bool bjdata_draft3 = use_bjdata && bjdata_version == bjdata_version_t::draft3; + switch (j.type()) { case value_t::null: @@ -15869,7 +16671,7 @@ class binary_writer for (const auto& el : *j.m_data.m_value.array) { - write_ubjson(el, use_count, use_type, prefix_required, use_bjdata); + write_ubjson(el, use_count, use_type, prefix_required, use_bjdata, bjdata_version); } if (!use_count) @@ -15887,11 +16689,11 @@ class binary_writer oa->write_character(to_char_type('[')); } - if (use_type && !j.m_data.m_value.binary->empty()) + if (use_type && (bjdata_draft3 || !j.m_data.m_value.binary->empty())) { JSON_ASSERT(use_count); oa->write_character(to_char_type('$')); - oa->write_character('U'); + oa->write_character(bjdata_draft3 ? 'B' : 'U'); } if (use_count) @@ -15910,7 +16712,7 @@ class binary_writer { for (size_t i = 0; i < j.m_data.m_value.binary->size(); ++i) { - oa->write_character(to_char_type('U')); + oa->write_character(to_char_type(bjdata_draft3 ? 'B' : 'U')); oa->write_character(j.m_data.m_value.binary->data()[i]); } } @@ -15927,7 +16729,7 @@ class binary_writer { if (use_bjdata && j.m_data.m_value.object->size() == 3 && j.m_data.m_value.object->find("_ArrayType_") != j.m_data.m_value.object->end() && j.m_data.m_value.object->find("_ArraySize_") != j.m_data.m_value.object->end() && j.m_data.m_value.object->find("_ArrayData_") != j.m_data.m_value.object->end()) { - if (!write_bjdata_ndarray(*j.m_data.m_value.object, use_count, use_type)) // decode bjdata ndarray in the JData format (https://github.com/NeuroJSON/jdata) + if (!write_bjdata_ndarray(*j.m_data.m_value.object, use_count, use_type, bjdata_version)) // decode bjdata ndarray in the JData format (https://github.com/NeuroJSON/jdata) { break; } @@ -15971,7 +16773,7 @@ class binary_writer oa->write_characters( reinterpret_cast(el.first.c_str()), el.first.size()); - write_ubjson(el.second, use_count, use_type, prefix_required, use_bjdata); + write_ubjson(el.second, use_count, use_type, prefix_required, use_bjdata, bjdata_version); } if (!use_count) @@ -16003,9 +16805,9 @@ class binary_writer if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) { JSON_THROW(out_of_range::create(409, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j)); - static_cast(j); } + static_cast(j); return /*id*/ 1ul + name.size() + /*zero-terminator*/1u; } @@ -16127,7 +16929,8 @@ class binary_writer } else { - JSON_THROW(out_of_range::create(407, concat("integer number ", std::to_string(j.m_data.m_value.number_unsigned), " cannot be represented by BSON as it does not fit int64"), &j)); + write_bson_entry_header(name, 0x11 /* uint64 */); + write_number(static_cast(j.m_data.m_value.number_unsigned), true); } } @@ -16581,7 +17384,7 @@ class binary_writer { return 'L'; } - // anything else is treated as high-precision number + // anything else is treated as a high-precision number return 'H'; // LCOV_EXCL_LINE } @@ -16619,7 +17422,7 @@ class binary_writer { return 'M'; } - // anything else is treated as high-precision number + // anything else is treated as a high-precision number return 'H'; // LCOV_EXCL_LINE } @@ -16655,10 +17458,11 @@ class binary_writer /*! @return false if the object is successfully converted to a bjdata ndarray, true if the type or size is invalid */ - bool write_bjdata_ndarray(const typename BasicJsonType::object_t& value, const bool use_count, const bool use_type) + bool write_bjdata_ndarray(const typename BasicJsonType::object_t& value, const bool use_count, const bool use_type, const bjdata_version_t bjdata_version) { std::map bjdtype = {{"uint8", 'U'}, {"int8", 'i'}, {"uint16", 'u'}, {"int16", 'I'}, - {"uint32", 'm'}, {"int32", 'l'}, {"uint64", 'M'}, {"int64", 'L'}, {"single", 'd'}, {"double", 'D'}, {"char", 'C'} + {"uint32", 'm'}, {"int32", 'l'}, {"uint64", 'M'}, {"int64", 'L'}, {"single", 'd'}, {"double", 'D'}, + {"char", 'C'}, {"byte", 'B'} }; string_t key = "_ArrayType_"; @@ -16688,10 +17492,10 @@ class binary_writer oa->write_character('#'); key = "_ArraySize_"; - write_ubjson(value.at(key), use_count, use_type, true, true); + write_ubjson(value.at(key), use_count, use_type, true, true, bjdata_version); key = "_ArrayData_"; - if (dtype == 'U' || dtype == 'C') + if (dtype == 'U' || dtype == 'C' || dtype == 'B') { for (const auto& el : value.at(key)) { @@ -16784,11 +17588,11 @@ class binary_writer template void write_number(const NumberType n, const bool OutputIsLittleEndian = false) { - // step 1: write number to array of length NumberType + // step 1: write the number to an array of length NumberType std::array vec{}; std::memcpy(vec.data(), &n, sizeof(NumberType)); - // step 2: write array to output (with possible reordering) + // step 2: write the array to output (with possible reordering) if (is_little_endian != OutputIsLittleEndian) { // reverse byte order prior to conversion if necessary @@ -16804,9 +17608,9 @@ class binary_writer #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wfloat-equal" #endif - if (static_cast(n) >= static_cast(std::numeric_limits::lowest()) && - static_cast(n) <= static_cast((std::numeric_limits::max)()) && - static_cast(static_cast(n)) == static_cast(n)) + if (!std::isfinite(n) || ((static_cast(n) >= static_cast(std::numeric_limits::lowest()) && + static_cast(n) <= static_cast((std::numeric_limits::max)()) && + static_cast(static_cast(n)) == static_cast(n)))) { oa->write_character(format == detail::input_format_t::cbor ? get_cbor_float_prefix(static_cast(n)) @@ -16841,8 +17645,21 @@ class binary_writer enable_if_t < std::is_signed::value && std::is_unsigned::value > * = nullptr > static CharType to_char_type(std::uint8_t x) noexcept { - static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t"); + // The std::is_trivial trait is deprecated in C++26. The replacement is to use + // std::is_trivially_copyable and std::is_trivially_default_constructible. + // However, some older library implementations support std::is_trivial + // but not all the std::is_trivially_* traits. + // Since detecting full support across all libraries is difficult, + // we use std::is_trivial unless we are using a standard where it has been deprecated. + // For more details, see: https://github.com/nlohmann/json/pull/4775#issuecomment-2884361627 +#ifdef JSON_HAS_CPP_26 + static_assert(std::is_trivially_copyable::value, "CharType must be trivially copyable"); + static_assert(std::is_trivially_default_constructible::value, "CharType must be trivially default constructible"); +#else static_assert(std::is_trivial::value, "CharType must be trivial"); +#endif + + static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t"); CharType result; std::memcpy(&result, &x, sizeof(x)); return result; @@ -16882,11 +17699,11 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2008-2009 Björn Hoehrmann -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2008, 2009 Björn Hoehrmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -16907,11 +17724,11 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // // SPDX-FileCopyrightText: 2009 Florian Loitsch -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -17038,7 +17855,7 @@ struct diyfp // f * 2^e // p_lo = p0_lo + (Q << 32) // // But in this particular case here, the full p_lo is not required. - // Effectively we only need to add the highest bit in p_lo to p_hi (and + // Effectively, we only need to add the highest bit in p_lo to p_hi (and // Q_hi + 1 does not overflow). Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up @@ -17128,7 +17945,7 @@ boundaries compute_boundaries(FloatType value) // Compute the boundaries m- and m+ of the floating-point value // v = f * 2^e. // - // Determine v- and v+, the floating-point predecessor and successor if v, + // Determine v- and v+, the floating-point predecessor and successor of v, // respectively. // // v- = v - 2^e if f != 2^(p-1) or e == e_min (A) @@ -17147,10 +17964,10 @@ boundaries compute_boundaries(FloatType value) // v- m- v m+ v+ const bool lower_boundary_is_closer = F == 0 && E > 1; - const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); + const diyfp m_plus = diyfp((2 * v.f) + 1, v.e - 1); const diyfp m_minus = lower_boundary_is_closer - ? diyfp(4 * v.f - 1, v.e - 2) // (B) - : diyfp(2 * v.f - 1, v.e - 1); // (A) + ? diyfp((4 * v.f) - 1, v.e - 2) // (B) + : diyfp((2 * v.f) - 1, v.e - 1); // (A) // Determine the normalized w+ = m+. const diyfp w_plus = diyfp::normalize(m_plus); @@ -17283,7 +18100,7 @@ inline cached_power get_cached_power_for_binary_exponent(int e) // (A smaller distance gamma-alpha would require a larger table.) // NB: - // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34. + // Actually, this function returns c, such that -60 <= e_c + e + 64 <= -34. constexpr int kCachedPowersMinDecExp = -300; constexpr int kCachedPowersDecStep = 8; @@ -17380,7 +18197,7 @@ inline cached_power get_cached_power_for_binary_exponent(int e) JSON_ASSERT(e >= -1500); JSON_ASSERT(e <= 1500); const int f = kAlpha - e - 1; - const int k = (f * 78913) / (1 << 18) + static_cast(f > 0); + const int k = ((f * 78913) / (1 << 18)) + static_cast(f > 0); const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; JSON_ASSERT(index >= 0); @@ -17595,8 +18412,8 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, decimal_exponent += n; - // We may now just stop. But instead look if the buffer could be - // decremented to bring V closer to w. + // We may now just stop. But instead, it looks as if the buffer + // could be decremented to bring V closer to w. // // pow10 = 10^n is now 1 ulp in the decimal representation V. // The rounding procedure works with diyfp's with an implicit @@ -17858,15 +18675,15 @@ inline char* append_exponent(char* buf, int e) } else if (k < 100) { - *buf++ = static_cast('0' + k / 10); + *buf++ = static_cast('0' + (k / 10)); k %= 10; *buf++ = static_cast('0' + k); } else { - *buf++ = static_cast('0' + k / 100); + *buf++ = static_cast('0' + (k / 100)); k %= 100; - *buf++ = static_cast('0' + k / 10); + *buf++ = static_cast('0' + (k / 10)); k %= 10; *buf++ = static_cast('0' + k); } @@ -18003,7 +18820,7 @@ char* to_chars(char* first, const char* last, FloatType value) // Compute v = buffer * 10^decimal_exponent. // The decimal digits are stored in the buffer, which needs to be interpreted // as an unsigned decimal integer. - // len is the length of the buffer, i.e. the number of decimal digits. + // len is the length of the buffer, i.e., the number of decimal digits. int len = 0; int decimal_exponent = 0; dtoa_impl::grisu2(first, len, decimal_exponent, value); @@ -18084,7 +18901,7 @@ class serializer , error_handler(error_handler_) {} - // delete because of pointer members + // deleted because of pointer members serializer(const serializer&) = delete; serializer& operator=(const serializer&) = delete; serializer(serializer&&) = delete; @@ -18582,7 +19399,7 @@ class serializer break; } - default: // decode found yet incomplete multi-byte code point + default: // decode found yet incomplete multibyte code point { if (!ensure_ascii) { @@ -18652,7 +19469,7 @@ class serializer @param[in] x unsigned integer number to count its digits @return number of decimal digits */ - inline unsigned int count_digits(number_unsigned_t x) noexcept + unsigned int count_digits(number_unsigned_t x) noexcept { unsigned int n_digits = 1; for (;;) @@ -18771,7 +19588,7 @@ class serializer // jump to the end to generate the string from backward, // so we later avoid reversing the result - buffer_ptr += n_chars; + buffer_ptr += static_cast(n_chars); // Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu // See: https://www.youtube.com/watch?v=o4-CwDo2zpg @@ -18836,7 +19653,7 @@ class serializer void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/) { - // get number of digits for a float -> text -> float round-trip + // get the number of digits for a float -> text -> float round-trip static constexpr auto d = std::numeric_limits::max_digits10; // the actual conversion @@ -18845,10 +19662,10 @@ class serializer // negative value indicates an error JSON_ASSERT(len > 0); - // check if buffer was large enough + // check if the buffer was large enough JSON_ASSERT(static_cast(len) < number_buffer.size()); - // erase thousands separator + // erase thousands separators if (thousands_sep != '\0') { // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081 @@ -18935,7 +19752,7 @@ class serializer ? (byte & 0x3fu) | (codep << 6u) : (0xFFu >> type) & (byte); - const std::size_t index = 256u + static_cast(state) * 16u + static_cast(type); + const std::size_t index = 256u + (static_cast(state) * 16u) + static_cast(type); JSON_ASSERT(index < utf8d.size()); state = utf8d[index]; return state; @@ -18956,12 +19773,12 @@ class serializer * Helper function for dump_integer * * This function takes a negative signed integer and returns its absolute - * value as unsigned integer. The plus/minus shuffling is necessary as we can - * not directly remove the sign of an arbitrary signed integer as the + * value as an unsigned integer. The plus/minus shuffling is necessary as we + * cannot directly remove the sign of an arbitrary signed integer as the * absolute values of INT_MIN and INT_MAX are usually not the same. See * #1708 for details. */ - inline number_unsigned_t remove_sign(number_integer_t x) noexcept + number_unsigned_t remove_sign(number_integer_t x) noexcept { JSON_ASSERT(x < 0 && x < (std::numeric_limits::max)()); // NOLINT(misc-redundant-expression) return static_cast(-(x + 1)) + 1; @@ -19003,10 +19820,10 @@ NLOHMANN_JSON_NAMESPACE_END // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -19031,7 +19848,7 @@ NLOHMANN_JSON_NAMESPACE_BEGIN /// for use within nlohmann::basic_json template , class Allocator = std::allocator>> - struct ordered_map : std::vector, Allocator> + struct ordered_map : std::vector, Allocator> { using key_type = Key; using mapped_type = T; @@ -19231,7 +20048,7 @@ template , // Since we cannot move const Keys, we re-construct them in place. // We start at first and re-construct (viz. copy) the elements from - // the back of the vector. Example for first iteration: + // the back of the vector. Example for the first iteration: // ,--------. // v | destroy e and re-construct with h @@ -19346,7 +20163,7 @@ template , template using require_input_iter = typename std::enable_if::iterator_category, - std::input_iterator_tag>::value>::type; + std::input_iterator_tag>::value>::type; template> void insert(InputIt first, InputIt last) @@ -19417,9 +20234,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec friend class ::nlohmann::detail::binary_writer; template friend class ::nlohmann::detail::binary_reader; - template + template friend class ::nlohmann::detail::json_sax_dom_parser; - template + template friend class ::nlohmann::detail::json_sax_dom_callback_parser; friend class ::nlohmann::detail::exception; @@ -19436,11 +20253,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec InputAdapterType adapter, detail::parser_callback_tcb = 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(std::move(adapter), - std::move(cb), allow_exceptions, ignore_comments); + std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas); } private: @@ -19473,6 +20291,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec using error_handler_t = detail::error_handler_t; /// how to treat CBOR tags using cbor_tag_handler_t = detail::cbor_tag_handler_t; + /// how to encode BJData + using bjdata_version_t = detail::bjdata_version_t; /// helper type for initializer lists of basic_json values using initializer_list_t = std::initializer_list>; @@ -19552,7 +20372,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { basic_json result; - result["copyright"] = "(C) 2013-2023 Niels Lohmann"; + result["copyright"] = "(C) 2013-2026 Niels Lohmann"; result["name"] = "JSON for Modern C++"; result["url"] = "https://github.com/nlohmann/json"; result["version"]["string"] = @@ -19817,7 +20637,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec object = nullptr; // silence warning, see #821 if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) { - JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.11.3", nullptr)); // LCOV_EXCL_LINE + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.12.0", nullptr)); // LCOV_EXCL_LINE } break; } @@ -19863,7 +20683,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec (t == value_t::binary && binary == nullptr) ) { - //not initialized (e.g. due to exception in the ctor) + // not initialized (e.g., due to exception in the ctor) return; } if (t == value_t::array || t == value_t::object) @@ -19888,7 +20708,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec while (!stack.empty()) { - // move the last item to local variable to be processed + // move the last item to a local variable to be processed basic_json current_item(std::move(stack.back())); stack.pop_back(); @@ -19910,7 +20730,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec current_item.m_data.m_value.object->clear(); } - // it's now safe that current_item get destructed + // it's now safe that current_item gets destructed // since it doesn't have any children } } @@ -20053,10 +20873,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return it; } - reference set_parent(reference j, std::size_t old_capacity = static_cast(-1)) + reference set_parent(reference j, std::size_t old_capacity = detail::unknown_size()) { #if JSON_DIAGNOSTICS - if (old_capacity != static_cast(-1)) + if (old_capacity != detail::unknown_size()) { // see https://github.com/nlohmann/json/issues/2838 JSON_ASSERT(type() == value_t::array); @@ -20136,8 +20956,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::enable_if_t < !detail::is_basic_json::value && detail::is_compatible_type::value, int > = 0 > basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape) - JSONSerializer::to_json(std::declval(), - std::forward(val)))) + JSONSerializer::to_json(std::declval(), + std::forward(val)))) { JSONSerializer::to_json(*this, std::forward(val)); set_parents(); @@ -20150,6 +20970,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::enable_if_t < detail::is_basic_json::value&& !std::is_same::value, int > = 0 > basic_json(const BasicJsonType& val) +#if JSON_DIAGNOSTIC_POSITIONS + : start_position(val.start_pos()), + end_position(val.end_pos()) +#endif { using other_boolean_t = typename BasicJsonType::boolean_t; using other_number_float_t = typename BasicJsonType::number_float_t; @@ -20196,6 +21020,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE } JSON_ASSERT(m_data.m_type == val.type()); + set_parents(); assert_invariant(); } @@ -20213,20 +21038,20 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { // The cast is to ensure op[size_type] is called, bearing in mind size_type may not be int; // (many string types can be constructed from 0 via its null-pointer guise, so we get a - // broken call to op[key_type], the wrong semantics and a 4804 warning on Windows) + // broken call to op[key_type], the wrong semantics, and a 4804 warning on Windows) return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[static_cast(0)].is_string(); }); // adjust type if type deduction is not wanted if (!type_deduction) { - // if array is wanted, do not create an object though possible + // if an array is wanted, do not create an object though possible if (manual_type == value_t::array) { is_an_object = false; } - // if object is wanted but impossible, throw an exception + // if an object is wanted but impossible, throw an exception if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object)) { JSON_THROW(type_error::create(301, "cannot create object from initializer list", nullptr)); @@ -20235,7 +21060,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (is_an_object) { - // the initializer list is a list of pairs -> create object + // the initializer list is a list of pairs -> create an object m_data.m_type = value_t::object; m_data.m_value = value_t::object; @@ -20249,7 +21074,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } else { - // the initializer list describes an array -> create array + // the initializer list describes an array -> create an array m_data.m_type = value_t::array; m_data.m_value.array = create(init.begin(), init.end()); } @@ -20332,21 +21157,21 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec template < class InputIT, typename std::enable_if < std::is_same::value || std::is_same::value, int >::type = 0 > - basic_json(InputIT first, InputIT last) + basic_json(InputIT first, InputIT last) // NOLINT(performance-unnecessary-value-param) { JSON_ASSERT(first.m_object != nullptr); JSON_ASSERT(last.m_object != nullptr); - // make sure iterator fits the current value + // make sure the iterator fits the current value if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", nullptr)); } - // copy type from first iterator + // copy type from the first iterator m_data.m_type = first.m_object->m_data.m_type; - // check if iterator range is complete for primitive values + // check if the iterator range is complete for primitive values switch (m_data.m_type) { case value_t::boolean: @@ -20447,6 +21272,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ basic_json(const basic_json& other) : json_base_class_t(other) +#if JSON_DIAGNOSTIC_POSITIONS + , start_position(other.start_position) + , end_position(other.end_position) +#endif { m_data.m_type = other.m_data.m_type; // check of passed value is valid @@ -20516,22 +21345,31 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ basic_json(basic_json&& other) noexcept : json_base_class_t(std::forward(other)), - m_data(std::move(other.m_data)) + m_data(std::move(other.m_data)) // cppcheck-suppress[accessForwarded] TODO check +#if JSON_DIAGNOSTIC_POSITIONS + , start_position(other.start_position) // cppcheck-suppress[accessForwarded] TODO check + , end_position(other.end_position) // cppcheck-suppress[accessForwarded] TODO check +#endif { - // check that passed value is valid - other.assert_invariant(false); + // check that the passed value is valid + other.assert_invariant(false); // cppcheck-suppress[accessForwarded] // invalidate payload other.m_data.m_type = value_t::null; other.m_data.m_value = {}; +#if JSON_DIAGNOSTIC_POSITIONS + other.start_position = std::string::npos; + other.end_position = std::string::npos; +#endif + set_parents(); assert_invariant(); } /// @brief copy assignment /// @sa https://json.nlohmann.me/api/basic_json/operator=/ - basic_json& operator=(basic_json other) noexcept ( + basic_json& operator=(basic_json other) noexcept ( // NOLINT(cppcoreguidelines-c-copy-assignment-signature,misc-unconventional-assign-operator) std::is_nothrow_move_constructible::value&& std::is_nothrow_move_assignable::value&& std::is_nothrow_move_constructible::value&& @@ -20539,12 +21377,18 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec std::is_nothrow_move_assignable::value ) { - // check that passed value is valid + // check that the passed value is valid other.assert_invariant(); using std::swap; swap(m_data.m_type, other.m_data.m_type); swap(m_data.m_value, other.m_data.m_value); + +#if JSON_DIAGNOSTIC_POSITIONS + swap(start_position, other.start_position); + swap(end_position, other.end_position); +#endif + json_base_class_t::operator=(std::move(other)); set_parents(); @@ -20766,13 +21610,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// get a pointer to the value (integer number) number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept { - return is_number_integer() ? &m_data.m_value.number_integer : nullptr; + return m_data.m_type == value_t::number_integer ? &m_data.m_value.number_integer : nullptr; } /// get a pointer to the value (integer number) constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept { - return is_number_integer() ? &m_data.m_value.number_integer : nullptr; + return m_data.m_type == value_t::number_integer ? &m_data.m_value.number_integer : nullptr; } /// get a pointer to the value (unsigned number) @@ -20907,7 +21751,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::has_from_json::value, int > = 0 > ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept( - JSONSerializer::from_json(std::declval(), std::declval()))) + JSONSerializer::from_json(std::declval(), std::declval()))) { auto ret = ValueType(); JSONSerializer::from_json(*this, ret); @@ -20949,7 +21793,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::has_non_default_from_json::value, int > = 0 > ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept( - JSONSerializer::from_json(std::declval()))) + JSONSerializer::from_json(std::declval()))) { return JSONSerializer::from_json(*this); } @@ -21099,7 +21943,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::has_from_json::value, int > = 0 > ValueType & get_to(ValueType& v) const noexcept(noexcept( - JSONSerializer::from_json(std::declval(), v))) + JSONSerializer::from_json(std::declval(), v))) { JSONSerializer::from_json(*this, v); return v; @@ -21249,9 +22093,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } JSON_CATCH (std::out_of_range&) { - // create better exception explanation + // create a better exception explanation JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this)); - } + } // cppcheck-suppress[missingReturn] } else { @@ -21272,9 +22116,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } JSON_CATCH (std::out_of_range&) { - // create better exception explanation + // create a better exception explanation JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this)); - } + } // cppcheck-suppress[missingReturn] } else { @@ -21362,7 +22206,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ reference operator[](size_type idx) { - // implicitly convert null value to an empty array + // implicitly convert a null value to an empty array if (is_null()) { m_data.m_type = value_t::array; @@ -21373,7 +22217,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // operator[] only works for arrays if (JSON_HEDLEY_LIKELY(is_array())) { - // fill up array with null values if given idx is outside range + // fill up the array with null values if given idx is outside the range if (idx >= m_data.m_value.array->size()) { #if JSON_DIAGNOSTICS @@ -21419,9 +22263,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief access specified object element /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ - reference operator[](typename object_t::key_type key) + reference operator[](typename object_t::key_type key) // NOLINT(performance-unnecessary-value-param) { - // implicitly convert null value to an empty object + // implicitly convert a null value to an empty object if (is_null()) { m_data.m_type = value_t::object; @@ -21474,7 +22318,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::is_usable_as_basic_json_key_type::value, int > = 0 > reference operator[](KeyType && key) { - // implicitly convert null value to an empty object + // implicitly convert a null value to an empty object if (is_null()) { m_data.m_type = value_t::object; @@ -21531,7 +22375,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // value only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - // if key is found, return value and given default value otherwise + // If 'key' is found, return its value. Otherwise, return `default_value'. const auto it = find(key); if (it != end()) { @@ -21556,7 +22400,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // value only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - // if key is found, return value and given default value otherwise + // If 'key' is found, return its value. Otherwise, return `default_value'. const auto it = find(key); if (it != end()) { @@ -21582,7 +22426,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // value only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - // if key is found, return value and given default value otherwise + // If 'key' is found, return its value. Otherwise, return `default_value'. const auto it = find(std::forward(key)); if (it != end()) { @@ -21609,7 +22453,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // value only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - // if key is found, return value and given default value otherwise + // If 'key' is found, return its value. Otherwise, return `default_value'. const auto it = find(std::forward(key)); if (it != end()) { @@ -21632,7 +22476,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // value only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - // if pointer resolves a value, return it or use default value + // If the pointer resolves to a value, return it. Otherwise, return + // 'default_value'. JSON_TRY { return ptr.get_checked(this).template get(); @@ -21657,7 +22502,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // value only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - // if pointer resolves a value, return it or use default value + // If the pointer resolves to a value, return it. Otherwise, return + // 'default_value'. JSON_TRY { return ptr.get_checked(this).template get(); @@ -21729,9 +22575,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec template < class IteratorType, detail::enable_if_t < std::is_same::value || std::is_same::value, int > = 0 > - IteratorType erase(IteratorType pos) + IteratorType erase(IteratorType pos) // NOLINT(performance-unnecessary-value-param) { - // make sure iterator fits the current value + // make sure the iterator fits the current value if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) { JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); @@ -21799,9 +22645,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec template < class IteratorType, detail::enable_if_t < std::is_same::value || std::is_same::value, int > = 0 > - IteratorType erase(IteratorType first, IteratorType last) + IteratorType erase(IteratorType first, IteratorType last) // NOLINT(performance-unnecessary-value-param) { - // make sure iterator fits the current value + // make sure the iterator fits the current value if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) { JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", this)); @@ -22396,7 +23242,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this)); } - // transform null object into an array + // transform a null object into an array if (is_null()) { m_data.m_type = value_t::array; @@ -22404,7 +23250,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec assert_invariant(); } - // add element to array (move semantics) + // add the element to the array (move semantics) const auto old_capacity = m_data.m_value.array->capacity(); m_data.m_value.array->push_back(std::move(val)); set_parent(m_data.m_value.array->back(), old_capacity); @@ -22429,7 +23275,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this)); } - // transform null object into an array + // transform a null object into an array if (is_null()) { m_data.m_type = value_t::array; @@ -22437,7 +23283,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec assert_invariant(); } - // add element to array + // add the element to the array const auto old_capacity = m_data.m_value.array->capacity(); m_data.m_value.array->push_back(val); set_parent(m_data.m_value.array->back(), old_capacity); @@ -22461,7 +23307,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this)); } - // transform null object into an object + // transform a null object into an object if (is_null()) { m_data.m_type = value_t::object; @@ -22469,7 +23315,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec assert_invariant(); } - // add element to object + // add the element to the object auto res = m_data.m_value.object->insert(val); set_parent(res.first->second); } @@ -22517,7 +23363,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(311, detail::concat("cannot use emplace_back() with ", type_name()), this)); } - // transform null object into an array + // transform a null object into an array if (is_null()) { m_data.m_type = value_t::array; @@ -22525,7 +23371,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec assert_invariant(); } - // add element to array (perfect forwarding) + // add the element to the array (perfect forwarding) const auto old_capacity = m_data.m_value.array->capacity(); m_data.m_value.array->emplace_back(std::forward(args)...); return set_parent(m_data.m_value.array->back(), old_capacity); @@ -22542,7 +23388,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(311, detail::concat("cannot use emplace() with ", type_name()), this)); } - // transform null object into an object + // transform a null object into an object if (is_null()) { m_data.m_type = value_t::object; @@ -22550,11 +23396,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec assert_invariant(); } - // add element to array (perfect forwarding) + // add the element to the array (perfect forwarding) auto res = m_data.m_value.object->emplace(std::forward(args)...); set_parent(res.first->second); - // create result iterator and set iterator to the result of emplace + // create a result iterator and set iterator to the result of emplace auto it = begin(); it.m_it.object_iterator = res.first; @@ -22566,7 +23412,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @note: This uses std::distance to support GCC 4.8, /// see https://github.com/nlohmann/json/pull/1257 template - iterator insert_iterator(const_iterator pos, Args&& ... args) + iterator insert_iterator(const_iterator pos, Args&& ... args) // NOLINT(performance-unnecessary-value-param) { iterator result(this); JSON_ASSERT(m_data.m_value.array != nullptr); @@ -22585,7 +23431,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief inserts element into array /// @sa https://json.nlohmann.me/api/basic_json/insert/ - iterator insert(const_iterator pos, const basic_json& val) + iterator insert(const_iterator pos, const basic_json& val) // NOLINT(performance-unnecessary-value-param) { // insert only works for arrays if (JSON_HEDLEY_LIKELY(is_array())) @@ -22605,14 +23451,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief inserts element into array /// @sa https://json.nlohmann.me/api/basic_json/insert/ - iterator insert(const_iterator pos, basic_json&& val) + iterator insert(const_iterator pos, basic_json&& val) // NOLINT(performance-unnecessary-value-param) { return insert(pos, val); } /// @brief inserts copies of element into array /// @sa https://json.nlohmann.me/api/basic_json/insert/ - iterator insert(const_iterator pos, size_type cnt, const basic_json& val) + iterator insert(const_iterator pos, size_type cnt, const basic_json& val) // NOLINT(performance-unnecessary-value-param) { // insert only works for arrays if (JSON_HEDLEY_LIKELY(is_array())) @@ -22632,7 +23478,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief inserts range of elements into array /// @sa https://json.nlohmann.me/api/basic_json/insert/ - iterator insert(const_iterator pos, const_iterator first, const_iterator last) + iterator insert(const_iterator pos, const_iterator first, const_iterator last) // NOLINT(performance-unnecessary-value-param) { // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) @@ -22663,7 +23509,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief inserts elements from initializer list into array /// @sa https://json.nlohmann.me/api/basic_json/insert/ - iterator insert(const_iterator pos, initializer_list_t ilist) + iterator insert(const_iterator pos, initializer_list_t ilist) // NOLINT(performance-unnecessary-value-param) { // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) @@ -22683,7 +23529,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief inserts range of elements into object /// @sa https://json.nlohmann.me/api/basic_json/insert/ - void insert(const_iterator first, const_iterator last) + void insert(const_iterator first, const_iterator last) // NOLINT(performance-unnecessary-value-param) { // insert only works for objects if (JSON_HEDLEY_UNLIKELY(!is_object())) @@ -22704,6 +23550,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } m_data.m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); + set_parents(); } /// @brief updates a JSON object from another object, overwriting existing keys @@ -22715,9 +23562,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief updates a JSON object from another object, overwriting existing keys /// @sa https://json.nlohmann.me/api/basic_json/update/ - void update(const_iterator first, const_iterator last, bool merge_objects = false) + void update(const_iterator first, const_iterator last, bool merge_objects = false) // NOLINT(performance-unnecessary-value-param) { - // implicitly convert null value to an empty object + // implicitly convert a null value to an empty object if (is_null()) { m_data.m_type = value_t::object; @@ -23277,7 +24124,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/ friend std::ostream& operator<<(std::ostream& o, const basic_json& j) { - // read width member and use it as indentation parameter if nonzero + // read width member and use it as the indentation parameter if nonzero const bool pretty_print = o.width() > 0; const auto indentation = pretty_print ? o.width() : 0; @@ -23316,12 +24163,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec template JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json parse(InputType&& i, - const parser_callback_t cb = nullptr, + 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(i)), cb, allow_exceptions, ignore_comments).parse(true, result); + parser(detail::input_adapter(std::forward(i)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved,accessForwarded] return result; } @@ -23331,24 +24179,26 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json parse(IteratorType first, IteratorType last, - const parser_callback_t cb = nullptr, + 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)), cb, allow_exceptions, ignore_comments).parse(true, result); + 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; } JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) static basic_json parse(detail::span_input_adapter&& i, - const parser_callback_t cb = nullptr, + 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(), cb, allow_exceptions, ignore_comments).parse(true, result); + parser(i.get(), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved] return result; } @@ -23356,26 +24206,29 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/accept/ template 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(i)), nullptr, false, ignore_comments).accept(true); + return parser(detail::input_adapter(std::forward(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 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 @@ -23385,11 +24238,28 @@ 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) { +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-pointer-compare" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull-compare" +#endif + if (sax == nullptr) + { + JSON_THROW(other_error::create(502, "SAX handler must not be null", nullptr)); + } +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif auto ia = detail::input_adapter(std::forward(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(std::move(ia), format).sax_parse(format, sax, strict); } @@ -23400,11 +24270,28 @@ 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) { +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-pointer-compare" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull-compare" +#endif + if (sax == nullptr) + { + JSON_THROW(other_error::create(502, "SAX handler must not be null", nullptr)); + } +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif 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(std::move(ia), format).sax_parse(format, sax, strict); } @@ -23419,12 +24306,29 @@ 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) { +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-pointer-compare" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull-compare" +#endif + if (sax == nullptr) + { + JSON_THROW(other_error::create(502, "SAX handler must not be null", nullptr)); + } +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif 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(std::move(ia), format).sax_parse(format, sax, strict); } @@ -23479,8 +24383,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case value_t::number_integer: case value_t::number_unsigned: case value_t::number_float: - default: return "number"; + default: + return "invalid"; } } @@ -23527,6 +24432,23 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec basic_json* m_parent = nullptr; #endif +#if JSON_DIAGNOSTIC_POSITIONS + /// the start position of the value + std::size_t start_position = std::string::npos; + /// the end position of the value + std::size_t end_position = std::string::npos; + public: + constexpr std::size_t start_pos() const noexcept + { + return start_position; + } + + constexpr std::size_t end_pos() const noexcept + { + return end_position; + } +#endif + ////////////////////////////////////////// // binary serialization/deserialization // ////////////////////////////////////////// @@ -23612,27 +24534,30 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/ static std::vector to_bjdata(const basic_json& j, const bool use_size = false, - const bool use_type = false) + const bool use_type = false, + const bjdata_version_t version = bjdata_version_t::draft2) { std::vector result; - to_bjdata(j, result, use_size, use_type); + to_bjdata(j, result, use_size, use_type, version); return result; } /// @brief create a BJData serialization of a given JSON value /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/ static void to_bjdata(const basic_json& j, detail::output_adapter o, - const bool use_size = false, const bool use_type = false) + const bool use_size = false, const bool use_type = false, + const bjdata_version_t version = bjdata_version_t::draft2) { - binary_writer(o).write_ubjson(j, use_size, use_type, true, true); + binary_writer(o).write_ubjson(j, use_size, use_type, true, true, version); } /// @brief create a BJData serialization of a given JSON value /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/ static void to_bjdata(const basic_json& j, detail::output_adapter o, - const bool use_size = false, const bool use_type = false) + const bool use_size = false, const bool use_type = false, + const bjdata_version_t version = bjdata_version_t::draft2) { - binary_writer(o).write_ubjson(j, use_size, use_type, true, true); + binary_writer(o).write_ubjson(j, use_size, use_type, true, true, version); } /// @brief create a BSON serialization of a given JSON value @@ -23668,9 +24593,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward(i)); - const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -23684,9 +24609,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); - const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -23709,10 +24634,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = i.get(); + detail::json_sax_dom_parser sdp(result, allow_exceptions); // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) - const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -23725,9 +24650,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward(i)); - const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -23740,9 +24665,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); - const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -23763,10 +24688,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = i.get(); + detail::json_sax_dom_parser sdp(result, allow_exceptions); // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) - const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -23779,9 +24704,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward(i)); - const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -23794,9 +24719,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); - const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -23817,10 +24742,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = i.get(); + detail::json_sax_dom_parser sdp(result, allow_exceptions); // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) - const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -23833,9 +24758,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward(i)); - const bool res = binary_reader(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -23848,9 +24773,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); - const bool res = binary_reader(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -23863,9 +24788,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward(i)); - const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -23878,9 +24803,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); - const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } @@ -23901,10 +24826,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true) { basic_json result; - detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = i.get(); + detail::json_sax_dom_parser sdp(result, allow_exceptions); // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) - const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved] return res ? result : basic_json(value_t::discarded); } /// @} @@ -24005,7 +24930,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // the valid JSON Patch operations enum class patch_operations {add, remove, replace, move, copy, test, invalid}; - const auto get_op = [](const std::string & op) + const auto get_op = [](const string_t& op) { if (op == "add") { @@ -24036,7 +24961,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec }; // wrapper for "add" operation; add value at ptr - const auto operation_add = [&result](json_pointer & ptr, basic_json val) + const auto operation_add = [&result](json_pointer & ptr, const basic_json & val) { // adding to the root of the target document means replacing it if (ptr.empty()) @@ -24052,7 +24977,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec result.at(top_pointer); } - // get reference to parent of JSON pointer ptr + // get reference to the parent of the JSON pointer ptr const auto last_path = ptr.back(); ptr.pop_back(); // parent must exist when performing patch add per RFC6902 specs @@ -24090,7 +25015,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec break; } - // if there exists a parent it cannot be primitive + // if there exists a parent, it cannot be primitive case value_t::string: // LCOV_EXCL_LINE case value_t::boolean: // LCOV_EXCL_LINE case value_t::number_integer: // LCOV_EXCL_LINE @@ -24106,7 +25031,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // wrapper for "remove" operation; remove value at ptr const auto operation_remove = [this, & result](json_pointer & ptr) { - // get reference to parent of JSON pointer ptr + // get reference to the parent of the JSON pointer ptr const auto last_path = ptr.back(); ptr.pop_back(); basic_json& parent = result.at(ptr); @@ -24142,8 +25067,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec for (const auto& val : json_patch) { // wrapper to get a value for an operation - const auto get_value = [&val](const std::string & op, - const std::string & member, + const auto get_value = [&val](const string_t& op, + const string_t& member, bool string_type) -> basic_json & { // find value @@ -24152,14 +25077,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // context-sensitive error message const auto error_msg = (op == "op") ? "operation" : detail::concat("operation '", op, '\''); // NOLINT(bugprone-unused-local-non-trivial-variable) - // check if desired value is present + // check if the desired value is present if (JSON_HEDLEY_UNLIKELY(it == val.m_data.m_value.object->end())) { // NOLINTNEXTLINE(performance-inefficient-string-concatenation) JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have member '", member, "'"), &val)); } - // check if result is of type string + // check if the result is of type string if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) { // NOLINTNEXTLINE(performance-inefficient-string-concatenation) @@ -24177,8 +25102,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } // collect mandatory members - const auto op = get_value("op", "op", true).template get(); - const auto path = get_value(op, "path", true).template get(); + const auto op = get_value("op", "op", true).template get(); + const auto path = get_value(op, "path", true).template get(); json_pointer ptr(path); switch (get_op(op)) @@ -24204,7 +25129,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case patch_operations::move: { - const auto from_path = get_value("move", "from", true).template get(); + const auto from_path = get_value("move", "from", true).template get(); json_pointer from_ptr(from_path); // the "from" location must exist - use at() @@ -24221,7 +25146,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case patch_operations::copy: { - const auto from_path = get_value("copy", "from", true).template get(); + const auto from_path = get_value("copy", "from", true).template get(); const json_pointer from_ptr(from_path); // the "from" location must exist - use at() @@ -24248,7 +25173,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // ignore out of range errors: success remains false } - // throw an exception if test fails + // throw an exception if the test fails if (JSON_HEDLEY_UNLIKELY(!success)) { JSON_THROW(other_error::create(501, detail::concat("unsuccessful: ", val.dump()), &val)); @@ -24281,12 +25206,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/diff/ JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json diff(const basic_json& source, const basic_json& target, - const std::string& path = "") + const string_t& path = "") { // the patch basic_json result(value_t::array); - // if the values are the same, return empty patch + // if the values are the same, return an empty patch if (source == target) { return result; @@ -24311,7 +25236,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec while (i < source.size() && i < target.size()) { // recursive call to compare array values at index i - auto temp_diff = diff(source[i], target[i], detail::concat(path, '/', std::to_string(i))); + auto temp_diff = diff(source[i], target[i], detail::concat(path, '/', detail::to_string(i))); result.insert(result.end(), temp_diff.begin(), temp_diff.end()); ++i; } @@ -24328,7 +25253,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec result.insert(result.begin() + end_index, object( { {"op", "remove"}, - {"path", detail::concat(path, '/', std::to_string(i))} + {"path", detail::concat(path, '/', detail::to_string(i))} })); ++i; } @@ -24339,7 +25264,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec result.push_back( { {"op", "add"}, - {"path", detail::concat(path, "/-")}, + {"path", detail::concat(path, "/-")}, {"value", target[i]} }); ++i; @@ -24354,7 +25279,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec for (auto it = source.cbegin(); it != source.cend(); ++it) { // escape the key name to be used in a JSON patch - const auto path_key = detail::concat(path, '/', detail::escape(it.key())); + const auto path_key = detail::concat(path, '/', detail::escape(it.key())); if (target.find(it.key()) != target.end()) { @@ -24378,7 +25303,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (source.find(it.key()) == source.end()) { // found a key that is not in this -> add it - const auto path_key = detail::concat(path, '/', detail::escape(it.key())); + const auto path_key = detail::concat(path, '/', detail::escape(it.key())); result.push_back( { {"op", "add"}, {"path", path_key}, @@ -24400,7 +25325,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case value_t::discarded: default: { - // both primitive type: replace value + // both primitive types: replace value result.push_back( { {"op", "replace"}, {"path", path}, {"value", target} @@ -24468,26 +25393,44 @@ inline namespace json_literals /// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/ JSON_HEDLEY_NON_NULL(1) #if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) - inline nlohmann::json operator ""_json(const char* s, std::size_t n) + inline nlohmann::json operator""_json(const char* s, std::size_t n) #else - inline nlohmann::json operator "" _json(const char* s, std::size_t n) + // GCC 4.8 requires a space between "" and suffix + inline nlohmann::json operator"" _json(const char* s, std::size_t n) #endif { return nlohmann::json::parse(s, s + n); } +#if defined(__cpp_char8_t) +JSON_HEDLEY_NON_NULL(1) +inline nlohmann::json operator""_json(const char8_t* s, std::size_t n) +{ + return nlohmann::json::parse(reinterpret_cast(s), + reinterpret_cast(s) + n); +} +#endif + /// @brief user-defined string literal for JSON pointer /// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/ JSON_HEDLEY_NON_NULL(1) #if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) - inline nlohmann::json::json_pointer operator ""_json_pointer(const char* s, std::size_t n) + inline nlohmann::json::json_pointer operator""_json_pointer(const char* s, std::size_t n) #else - inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) + // GCC 4.8 requires a space between "" and suffix + inline nlohmann::json::json_pointer operator"" _json_pointer(const char* s, std::size_t n) #endif { return nlohmann::json::json_pointer(std::string(s, n)); } +#if defined(__cpp_char8_t) +inline nlohmann::json::json_pointer operator""_json_pointer(const char8_t* s, std::size_t n) +{ + return nlohmann::json::json_pointer(std::string(reinterpret_cast(s), n)); +} +#endif + } // namespace json_literals } // namespace literals NLOHMANN_JSON_NAMESPACE_END @@ -24548,21 +25491,22 @@ inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC #if JSON_USE_GLOBAL_UDLS #if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) - using nlohmann::literals::json_literals::operator ""_json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers) - using nlohmann::literals::json_literals::operator ""_json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers) + using nlohmann::literals::json_literals::operator""_json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers) + using nlohmann::literals::json_literals::operator""_json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers) #else - using nlohmann::literals::json_literals::operator "" _json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers) - using nlohmann::literals::json_literals::operator "" _json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers) + // GCC 4.8 requires a space between "" and suffix + using nlohmann::literals::json_literals::operator"" _json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers) + using nlohmann::literals::json_literals::operator"" _json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers) #endif #endif // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -24593,6 +25537,8 @@ inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC #undef JSON_HAS_CPP_14 #undef JSON_HAS_CPP_17 #undef JSON_HAS_CPP_20 + #undef JSON_HAS_CPP_23 + #undef JSON_HAS_CPP_26 #undef JSON_HAS_FILESYSTEM #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM #undef JSON_HAS_THREE_WAY_COMPARISON @@ -24604,10 +25550,10 @@ inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT diff --git a/single_include/nlohmann/json_fwd.hpp b/single_include/nlohmann/json_fwd.hpp index 29a6036d79..525e65b642 100644 --- a/single_include/nlohmann/json_fwd.hpp +++ b/single_include/nlohmann/json_fwd.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ @@ -18,10 +18,10 @@ // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT @@ -30,20 +30,24 @@ #ifndef JSON_SKIP_LIBRARY_VERSION_CHECK #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) - #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 3 + #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 12 || NLOHMANN_JSON_VERSION_PATCH != 0 #warning "Already included a different version of the library!" #endif #endif #endif #define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) -#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum) -#define NLOHMANN_JSON_VERSION_PATCH 3 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_MINOR 12 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_PATCH 0 // NOLINT(modernize-macro-to-enum) #ifndef JSON_DIAGNOSTICS #define JSON_DIAGNOSTICS 0 #endif +#ifndef JSON_DIAGNOSTIC_POSITIONS + #define JSON_DIAGNOSTIC_POSITIONS 0 +#endif + #ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 #endif @@ -54,6 +58,12 @@ #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS #endif +#if JSON_DIAGNOSTIC_POSITIONS + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS _dp +#else + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS +#endif + #if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp #else @@ -65,14 +75,15 @@ #endif // Construct the namespace ABI tags component -#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b -#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \ - NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) +#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) json_abi ## a ## b ## c +#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c) \ + NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) #define NLOHMANN_JSON_ABI_TAGS \ NLOHMANN_JSON_ABI_TAGS_CONCAT( \ NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ - NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON) + NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \ + NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS) // Construct the namespace version component #define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt new file mode 100644 index 0000000000..f8b79d0ef6 --- /dev/null +++ b/src/modules/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.28) + +add_library(nlohmann_json_modules) + +set(NLOHMANN_JSON_MODULES + json.cppm +) + +if(NOT COMMAND configure_cpp_module_target) + function(configure_cpp_module_target target) + target_sources(${target} PUBLIC FILE_SET CXX_MODULES FILES ${NLOHMANN_JSON_MODULES}) + endfunction() +endif() + +configure_cpp_module_target(nlohmann_json_modules) + +target_link_libraries(nlohmann_json_modules + PUBLIC + nlohmann_json::nlohmann_json +) + +target_include_directories(nlohmann_json_modules + PRIVATE + ${PROJECT_SOURCE_DIR}/include +) + +target_compile_features(nlohmann_json_modules PUBLIC cxx_std_20) diff --git a/src/modules/json.cppm b/src/modules/json.cppm new file mode 100644 index 0000000000..623d8cb81f --- /dev/null +++ b/src/modules/json.cppm @@ -0,0 +1,32 @@ +module; + +#include + +export module nlohmann.json; + +export namespace nlohmann { + using ::nlohmann::adl_serializer; + using ::nlohmann::basic_json; + using ::nlohmann::json; + using ::nlohmann::json_pointer; + using ::nlohmann::ordered_json; + using ::nlohmann::ordered_map; +} // namespace nlohmann + +NLOHMANN_JSON_NAMESPACE_BEGIN + +namespace detail +{ + export using NLOHMANN_JSON_NAMESPACE::detail::json_sax_dom_callback_parser; + export using NLOHMANN_JSON_NAMESPACE::detail::unknown_size; +} // namespace detail + +export using NLOHMANN_JSON_NAMESPACE::adl_serializer; +export using NLOHMANN_JSON_NAMESPACE::basic_json; +export using NLOHMANN_JSON_NAMESPACE::json; +export using NLOHMANN_JSON_NAMESPACE::json_pointer; +export using NLOHMANN_JSON_NAMESPACE::ordered_json; +export using NLOHMANN_JSON_NAMESPACE::ordered_map; +export using NLOHMANN_JSON_NAMESPACE::to_string; + +NLOHMANN_JSON_NAMESPACE_END diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a153a69248..b1dc5df104 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -21,12 +21,21 @@ include(test) # override standard support ############################################################################# +# Clang only supports C++14 starting from Clang 3.5 (lesser versions miss std::enable_if_t) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5) + unset(compiler_supports_cpp_14) +endif() + # Clang only supports C++17 starting from Clang 5.0 if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) unset(compiler_supports_cpp_17) endif() # MSVC 2015 (14.0) does not support C++17 -if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.1) +if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.10) + unset(compiler_supports_cpp_17) +endif() +# GCC 5 and 6 do claim experimental support for C++17, but do not implement +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0) unset(compiler_supports_cpp_17) endif() @@ -34,6 +43,10 @@ endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) unset(compiler_supports_cpp_20) endif() +# MSVC 2017 (15.x) does not support C++20 +if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.20) + unset(compiler_supports_cpp_20) +endif() # GCC started supporting C++20 features in 8.0 but a test for #3070 segfaults prior to 9.0 if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) unset(compiler_supports_cpp_20) diff --git a/tests/abi/config/config.hpp b/tests/abi/config/config.hpp index 8762b2a6ba..63681899f8 100644 --- a/tests/abi/config/config.hpp +++ b/tests/abi/config/config.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once diff --git a/tests/abi/config/custom.cpp b/tests/abi/config/custom.cpp index 9e5bb89804..0097747c98 100644 --- a/tests/abi/config/custom.cpp +++ b/tests/abi/config/custom.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/abi/config/default.cpp b/tests/abi/config/default.cpp index d54ca6ff39..0edc12e621 100644 --- a/tests/abi/config/default.cpp +++ b/tests/abi/config/default.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -24,6 +24,10 @@ TEST_CASE("default namespace") expected += "_diag"; #endif +#if JSON_DIAGNOSTIC_POSITIONS + expected += "_dp"; +#endif + #if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON expected += "_ldvcmp"; #endif diff --git a/tests/abi/config/noversion.cpp b/tests/abi/config/noversion.cpp index 2a7c277d78..2ae5cf5acd 100644 --- a/tests/abi/config/noversion.cpp +++ b/tests/abi/config/noversion.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -25,6 +25,10 @@ TEST_CASE("default namespace without version component") expected += "_diag"; #endif +#if JSON_DIAGNOSTIC_POSITIONS + expected += "_dp"; +#endif + #if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON expected += "_ldvcmp"; #endif diff --git a/tests/abi/diag/diag.cpp b/tests/abi/diag/diag.cpp index 286f75dbe9..ee32240e31 100644 --- a/tests/abi/diag/diag.cpp +++ b/tests/abi/diag/diag.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/abi/diag/diag.hpp b/tests/abi/diag/diag.hpp index 1d4d49e9a6..478cae8ce9 100644 --- a/tests/abi/diag/diag.hpp +++ b/tests/abi/diag/diag.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once diff --git a/tests/abi/diag/diag_off.cpp b/tests/abi/diag/diag_off.cpp index cd0d178f74..47633c9af4 100644 --- a/tests/abi/diag/diag_off.cpp +++ b/tests/abi/diag/diag_off.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #undef JSON_DIAGNOSTICS diff --git a/tests/abi/diag/diag_on.cpp b/tests/abi/diag/diag_on.cpp index 197a2c0c61..e1a94c4f66 100644 --- a/tests/abi/diag/diag_on.cpp +++ b/tests/abi/diag/diag_on.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #undef JSON_DIAGNOSTICS diff --git a/tests/abi/inline_ns/use_current.cpp b/tests/abi/inline_ns/use_current.cpp index cb07dd33e9..45d09f9c89 100644 --- a/tests/abi/inline_ns/use_current.cpp +++ b/tests/abi/inline_ns/use_current.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/abi/inline_ns/use_v3_10_5.cpp b/tests/abi/inline_ns/use_v3_10_5.cpp index 7a4275390a..a3d08985bb 100644 --- a/tests/abi/inline_ns/use_v3_10_5.cpp +++ b/tests/abi/inline_ns/use_v3_10_5.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/abi/main.cpp b/tests/abi/main.cpp index 0a4805e76a..93a934005b 100644 --- a/tests/abi/main.cpp +++ b/tests/abi/main.cpp @@ -1,10 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// Copyright (c) 2013-2022 Niels Lohmann . -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN diff --git a/tests/benchmarks/src/benchmarks.cpp b/tests/benchmarks/src/benchmarks.cpp index 0210b9feaf..4de2382723 100644 --- a/tests/benchmarks/src/benchmarks.cpp +++ b/tests/benchmarks/src/benchmarks.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include @@ -140,6 +140,46 @@ BENCHMARK_CAPTURE(ToCbor, signed_ints, TEST_DATA_DIRECTORY "/regression/si BENCHMARK_CAPTURE(ToCbor, unsigned_ints, TEST_DATA_DIRECTORY "/regression/unsigned_ints.json"); BENCHMARK_CAPTURE(ToCbor, small_signed_ints, TEST_DATA_DIRECTORY "/regression/small_signed_ints.json"); +////////////////////////////////////////////////////////////////////////////// +// Parse Msgpack +////////////////////////////////////////////////////////////////////////////// + +static void FromMsgpack(benchmark::State& state, const char* filename) +{ + std::ifstream f(filename); + std::string str((std::istreambuf_iterator(f)), std::istreambuf_iterator()); + auto bytes = json::to_msgpack(json::parse(str)); + std::ofstream o("test.msgpack"); + o.write((char*)bytes.data(), bytes.size()); + o.flush(); + o.close(); + for (auto _ : state) + { + state.PauseTiming(); + auto* j = new json(); + auto file = fopen("test.msgpack", "rb"); + state.ResumeTiming(); + + *j = json::from_msgpack(file); + + state.PauseTiming(); + fclose(file); + delete j; + state.ResumeTiming(); + } + + state.SetBytesProcessed(state.iterations() * bytes.size()); +} + +BENCHMARK_CAPTURE(FromMsgpack, jeopardy, TEST_DATA_DIRECTORY "/jeopardy/jeopardy.json"); +BENCHMARK_CAPTURE(FromMsgpack, canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json"); +BENCHMARK_CAPTURE(FromMsgpack, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json"); +BENCHMARK_CAPTURE(FromMsgpack, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json"); +BENCHMARK_CAPTURE(FromMsgpack, floats, TEST_DATA_DIRECTORY "/regression/floats.json"); +BENCHMARK_CAPTURE(FromMsgpack, signed_ints, TEST_DATA_DIRECTORY "/regression/signed_ints.json"); +BENCHMARK_CAPTURE(FromMsgpack, unsigned_ints, TEST_DATA_DIRECTORY "/regression/unsigned_ints.json"); +BENCHMARK_CAPTURE(FromMsgpack, small_signed_ints, TEST_DATA_DIRECTORY "/regression/small_signed_ints.json"); + ////////////////////////////////////////////////////////////////////////////// // serialize binary CBOR ////////////////////////////////////////////////////////////////////////////// diff --git a/tests/cmake_add_subdirectory/CMakeLists.txt b/tests/cmake_add_subdirectory/CMakeLists.txt index 21c736a7b8..7bb661075f 100644 --- a/tests/cmake_add_subdirectory/CMakeLists.txt +++ b/tests/cmake_add_subdirectory/CMakeLists.txt @@ -2,6 +2,7 @@ add_test(NAME cmake_add_subdirectory_configure COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -Dnlohmann_json_source=${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/project ) diff --git a/tests/cmake_add_subdirectory/project/CMakeLists.txt b/tests/cmake_add_subdirectory/project/CMakeLists.txt index 0695305ff9..ff8838e6d7 100644 --- a/tests/cmake_add_subdirectory/project/CMakeLists.txt +++ b/tests/cmake_add_subdirectory/project/CMakeLists.txt @@ -1,7 +1,11 @@ -cmake_minimum_required(VERSION 3.1...3.14) +cmake_minimum_required(VERSION 3.5...3.14) project(DummyImport CXX) +if(MSVC) + add_compile_options(/EHsc) +endif() + set(JSON_BuildTests OFF CACHE INTERNAL "") add_subdirectory(${nlohmann_json_source} ${CMAKE_CURRENT_BINARY_DIR}/nlohmann_json) diff --git a/tests/cmake_add_subdirectory/project/main.cpp b/tests/cmake_add_subdirectory/project/main.cpp index 6db9448617..95b4e161bd 100644 --- a/tests/cmake_add_subdirectory/project/main.cpp +++ b/tests/cmake_add_subdirectory/project/main.cpp @@ -1,14 +1,14 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include -int main(int argc, char** argv) +int main() { nlohmann::json j; diff --git a/tests/cmake_fetch_content/CMakeLists.txt b/tests/cmake_fetch_content/CMakeLists.txt index 7df00865e6..211a49cee9 100644 --- a/tests/cmake_fetch_content/CMakeLists.txt +++ b/tests/cmake_fetch_content/CMakeLists.txt @@ -3,6 +3,7 @@ if (${CMAKE_VERSION} VERSION_GREATER "3.11.0") COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -Dnlohmann_json_source=${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/project ) diff --git a/tests/cmake_fetch_content/project/CMakeLists.txt b/tests/cmake_fetch_content/project/CMakeLists.txt index b64af9ea5c..9f74881579 100644 --- a/tests/cmake_fetch_content/project/CMakeLists.txt +++ b/tests/cmake_fetch_content/project/CMakeLists.txt @@ -13,6 +13,10 @@ if(NOT json_POPULATED) add_subdirectory(${json_SOURCE_DIR} ${json_BINARY_DIR} EXCLUDE_FROM_ALL) endif() +if(MSVC) + add_compile_options(/EHsc) +endif() + add_executable(with_namespace_target main.cpp) target_link_libraries(with_namespace_target nlohmann_json::nlohmann_json) diff --git a/tests/cmake_fetch_content/project/main.cpp b/tests/cmake_fetch_content/project/main.cpp index 6db9448617..95b4e161bd 100644 --- a/tests/cmake_fetch_content/project/main.cpp +++ b/tests/cmake_fetch_content/project/main.cpp @@ -1,14 +1,14 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include -int main(int argc, char** argv) +int main() { nlohmann::json j; diff --git a/tests/cmake_fetch_content2/CMakeLists.txt b/tests/cmake_fetch_content2/CMakeLists.txt index 40e75e832e..b35f93cfdd 100644 --- a/tests/cmake_fetch_content2/CMakeLists.txt +++ b/tests/cmake_fetch_content2/CMakeLists.txt @@ -3,6 +3,7 @@ if (${CMAKE_VERSION} VERSION_GREATER "3.14.0") COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -Dnlohmann_json_source=${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/project ) diff --git a/tests/cmake_fetch_content2/project/CMakeLists.txt b/tests/cmake_fetch_content2/project/CMakeLists.txt index 734b9cc0ed..5a2f722e66 100644 --- a/tests/cmake_fetch_content2/project/CMakeLists.txt +++ b/tests/cmake_fetch_content2/project/CMakeLists.txt @@ -8,6 +8,10 @@ get_filename_component(GIT_REPOSITORY_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../. FetchContent_Declare(json GIT_REPOSITORY ${GIT_REPOSITORY_DIRECTORY} GIT_TAG HEAD) FetchContent_MakeAvailable(json) +if(MSVC) + add_compile_options(/EHsc) +endif() + add_executable(with_namespace_target main.cpp) target_link_libraries(with_namespace_target nlohmann_json::nlohmann_json) diff --git a/tests/cmake_fetch_content2/project/main.cpp b/tests/cmake_fetch_content2/project/main.cpp index 6db9448617..95b4e161bd 100644 --- a/tests/cmake_fetch_content2/project/main.cpp +++ b/tests/cmake_fetch_content2/project/main.cpp @@ -1,14 +1,14 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include -int main(int argc, char** argv) +int main() { nlohmann::json j; diff --git a/tests/cmake_import/CMakeLists.txt b/tests/cmake_import/CMakeLists.txt index 13290b2e3a..eec5263891 100644 --- a/tests/cmake_import/CMakeLists.txt +++ b/tests/cmake_import/CMakeLists.txt @@ -3,6 +3,7 @@ add_test(NAME cmake_import_configure -G "${CMAKE_GENERATOR}" -A "${CMAKE_GENERATOR_PLATFORM}" -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -Dnlohmann_json_DIR=${PROJECT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/project ) diff --git a/tests/cmake_import/project/CMakeLists.txt b/tests/cmake_import/project/CMakeLists.txt index 585f34cef3..181040c62d 100644 --- a/tests/cmake_import/project/CMakeLists.txt +++ b/tests/cmake_import/project/CMakeLists.txt @@ -1,12 +1,15 @@ -cmake_minimum_required(VERSION 3.1...3.14) +cmake_minimum_required(VERSION 3.5...3.14) project(DummyImport CXX) find_package(nlohmann_json REQUIRED) +if(MSVC) + add_compile_options(/EHsc) +endif() + add_executable(with_namespace_target main.cpp) target_link_libraries(with_namespace_target nlohmann_json::nlohmann_json) add_executable(without_namespace_target main.cpp) target_link_libraries(without_namespace_target nlohmann_json) - diff --git a/tests/cmake_import/project/main.cpp b/tests/cmake_import/project/main.cpp index 6db9448617..95b4e161bd 100644 --- a/tests/cmake_import/project/main.cpp +++ b/tests/cmake_import/project/main.cpp @@ -1,14 +1,14 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include -int main(int argc, char** argv) +int main() { nlohmann::json j; diff --git a/tests/cmake_import_minver/CMakeLists.txt b/tests/cmake_import_minver/CMakeLists.txt index 6d8db43995..42e3ff3e49 100644 --- a/tests/cmake_import_minver/CMakeLists.txt +++ b/tests/cmake_import_minver/CMakeLists.txt @@ -3,6 +3,7 @@ add_test(NAME cmake_import_minver_configure -G "${CMAKE_GENERATOR}" -A "${CMAKE_GENERATOR_PLATFORM}" -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -Dnlohmann_json_DIR=${PROJECT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/project ) diff --git a/tests/cmake_import_minver/project/CMakeLists.txt b/tests/cmake_import_minver/project/CMakeLists.txt index 25e40bfac2..0b8c90b25b 100644 --- a/tests/cmake_import_minver/project/CMakeLists.txt +++ b/tests/cmake_import_minver/project/CMakeLists.txt @@ -1,8 +1,12 @@ -cmake_minimum_required(VERSION 3.1...3.14) +cmake_minimum_required(VERSION 3.5...3.14) project(DummyImportMinVer CXX) find_package(nlohmann_json 3.2.0 REQUIRED) +if(MSVC) + add_compile_options(/EHsc) +endif() + add_executable(with_namespace_target main.cpp) target_link_libraries(with_namespace_target nlohmann_json::nlohmann_json) diff --git a/tests/cmake_import_minver/project/main.cpp b/tests/cmake_import_minver/project/main.cpp index 6db9448617..95b4e161bd 100644 --- a/tests/cmake_import_minver/project/main.cpp +++ b/tests/cmake_import_minver/project/main.cpp @@ -1,14 +1,14 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include -int main(int argc, char** argv) +int main() { nlohmann::json j; diff --git a/tests/cmake_target_include_directories/CMakeLists.txt b/tests/cmake_target_include_directories/CMakeLists.txt index 0a324cb922..86fea3a5a7 100644 --- a/tests/cmake_target_include_directories/CMakeLists.txt +++ b/tests/cmake_target_include_directories/CMakeLists.txt @@ -2,6 +2,7 @@ add_test(NAME cmake_target_include_directories_configure COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -Dnlohmann_json_source=${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/project ) diff --git a/tests/cmake_target_include_directories/project/Bar.cpp b/tests/cmake_target_include_directories/project/Bar.cpp index 83a14bc85c..c7e73d5670 100644 --- a/tests/cmake_target_include_directories/project/Bar.cpp +++ b/tests/cmake_target_include_directories/project/Bar.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "Bar.hpp" diff --git a/tests/cmake_target_include_directories/project/Bar.hpp b/tests/cmake_target_include_directories/project/Bar.hpp index a56257c308..5cbedfeabd 100644 --- a/tests/cmake_target_include_directories/project/Bar.hpp +++ b/tests/cmake_target_include_directories/project/Bar.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include diff --git a/tests/cmake_target_include_directories/project/CMakeLists.txt b/tests/cmake_target_include_directories/project/CMakeLists.txt index 26b55cd891..815cf52f51 100644 --- a/tests/cmake_target_include_directories/project/CMakeLists.txt +++ b/tests/cmake_target_include_directories/project/CMakeLists.txt @@ -1,13 +1,17 @@ -cmake_minimum_required(VERSION 3.1...3.14) +cmake_minimum_required(VERSION 3.5...3.14) project(DummyImport CXX) +if(MSVC) + add_compile_options(/EHsc) +endif() + add_executable(with_private_target main.cpp) target_include_directories(with_private_target PRIVATE ${nlohmann_json_source}/include) set_target_properties(with_private_target PROPERTIES CXX_STANDARD 11) add_executable(with_private_system_target main.cpp) -target_include_directories(with_private_system_target PRIVATE SYSTEM ${nlohmann_json_source}/include) +target_include_directories(with_private_system_target SYSTEM PRIVATE ${nlohmann_json_source}/include) set_target_properties(with_private_system_target PROPERTIES CXX_STANDARD 11) # regression from https://github.com/nlohmann/json/discussions/2281 diff --git a/tests/cmake_target_include_directories/project/Foo.cpp b/tests/cmake_target_include_directories/project/Foo.cpp index 42e950e751..4e270d387b 100644 --- a/tests/cmake_target_include_directories/project/Foo.cpp +++ b/tests/cmake_target_include_directories/project/Foo.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "Foo.hpp" diff --git a/tests/cmake_target_include_directories/project/Foo.hpp b/tests/cmake_target_include_directories/project/Foo.hpp index 18fe9763ed..60509b127a 100644 --- a/tests/cmake_target_include_directories/project/Foo.hpp +++ b/tests/cmake_target_include_directories/project/Foo.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once diff --git a/tests/cmake_target_include_directories/project/main.cpp b/tests/cmake_target_include_directories/project/main.cpp index 6db9448617..95b4e161bd 100644 --- a/tests/cmake_target_include_directories/project/main.cpp +++ b/tests/cmake_target_include_directories/project/main.cpp @@ -1,14 +1,14 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include -int main(int argc, char** argv) +int main() { nlohmann::json j; diff --git a/tests/cuda_example/json_cuda.cu b/tests/cuda_example/json_cuda.cu index 6575966620..a27378aec7 100644 --- a/tests/cuda_example/json_cuda.cu +++ b/tests/cuda_example/json_cuda.cu @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include diff --git a/tests/module_cpp20/CMakeLists.txt b/tests/module_cpp20/CMakeLists.txt new file mode 100644 index 0000000000..b6d395aaad --- /dev/null +++ b/tests/module_cpp20/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.28) + +project(json_test CXX) + +set(NLOHMANN_JSON_BUILD_MODULES ON CACHE BOOL "Enable nlohmann.json module support") + +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../.. ${CMAKE_CURRENT_BINARY_DIR}/tests) + +add_executable(json_test main.cpp) +target_link_libraries(json_test + PRIVATE + nlohmann_json_modules +) + +target_compile_definitions(json_test + PRIVATE NLOHMANN_JSON_BUILD_MODULES +) + +target_compile_features(json_test PUBLIC cxx_std_20) diff --git a/tests/module_cpp20/main.cpp b/tests/module_cpp20/main.cpp new file mode 100644 index 0000000000..9a2f3d360f --- /dev/null +++ b/tests/module_cpp20/main.cpp @@ -0,0 +1,14 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ (supporting code) +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + +import nlohmann.json; + +int main() +{ + nlohmann::json j; +} diff --git a/tests/src/fuzzer-driver_afl.cpp b/tests/src/fuzzer-driver_afl.cpp index ae3aefe1ee..1cde49bd8a 100644 --- a/tests/src/fuzzer-driver_afl.cpp +++ b/tests/src/fuzzer-driver_afl.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT /* diff --git a/tests/src/fuzzer-parse_bjdata.cpp b/tests/src/fuzzer-parse_bjdata.cpp index 30f1ddd004..1d1d56a5c4 100644 --- a/tests/src/fuzzer-parse_bjdata.cpp +++ b/tests/src/fuzzer-parse_bjdata.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT /* diff --git a/tests/src/fuzzer-parse_bson.cpp b/tests/src/fuzzer-parse_bson.cpp index fa2a221db9..c5f74c7cc8 100644 --- a/tests/src/fuzzer-parse_bson.cpp +++ b/tests/src/fuzzer-parse_bson.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT /* diff --git a/tests/src/fuzzer-parse_cbor.cpp b/tests/src/fuzzer-parse_cbor.cpp index db92aca8fd..b38e3c1e54 100644 --- a/tests/src/fuzzer-parse_cbor.cpp +++ b/tests/src/fuzzer-parse_cbor.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT /* diff --git a/tests/src/fuzzer-parse_json.cpp b/tests/src/fuzzer-parse_json.cpp index be04e80309..59a278c7bd 100644 --- a/tests/src/fuzzer-parse_json.cpp +++ b/tests/src/fuzzer-parse_json.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT /* diff --git a/tests/src/fuzzer-parse_msgpack.cpp b/tests/src/fuzzer-parse_msgpack.cpp index 0106b4053a..0b4ab0af71 100644 --- a/tests/src/fuzzer-parse_msgpack.cpp +++ b/tests/src/fuzzer-parse_msgpack.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT /* diff --git a/tests/src/fuzzer-parse_ubjson.cpp b/tests/src/fuzzer-parse_ubjson.cpp index 2830bef758..463656c71f 100644 --- a/tests/src/fuzzer-parse_ubjson.cpp +++ b/tests/src/fuzzer-parse_ubjson.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT /* diff --git a/tests/src/make_test_data_available.hpp b/tests/src/make_test_data_available.hpp index a2ffefa898..7df1a719af 100644 --- a/tests/src/make_test_data_available.hpp +++ b/tests/src/make_test_data_available.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once @@ -18,7 +18,8 @@ namespace utils inline bool check_testsuite_downloaded() { - const std::unique_ptr file(std::fopen(TEST_DATA_DIRECTORY "/README.md", "r"), &std::fclose); + using FilePtr = std::unique_ptr; + const FilePtr file(std::fopen(TEST_DATA_DIRECTORY "/README.md", "r"), std::fclose); return file != nullptr; } diff --git a/tests/src/test_utils.hpp b/tests/src/test_utils.hpp index dbb0a9b140..37afa21e8b 100644 --- a/tests/src/test_utils.hpp +++ b/tests/src/test_utils.hpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #pragma once diff --git a/tests/src/unit-32bit.cpp b/tests/src/unit-32bit.cpp index 22a2ad2835..e160304e88 100644 --- a/tests/src/unit-32bit.cpp +++ b/tests/src/unit-32bit.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -23,7 +23,7 @@ struct trait_test_arg static constexpr bool max_in_range = MaxInRange; }; -TEST_CASE_TEMPLATE_DEFINE("value_in_range_of trait", T, value_in_range_of_test) +TEST_CASE_TEMPLATE_DEFINE("value_in_range_of trait", T, value_in_range_of_test) // NOLINT(readability-math-missing-parentheses) { using nlohmann::detail::value_in_range_of; diff --git a/tests/src/unit-algorithms.cpp b/tests/src/unit-algorithms.cpp index 3fb1640d45..0aac52f0bc 100644 --- a/tests/src/unit-algorithms.cpp +++ b/tests/src/unit-algorithms.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit-allocator.cpp b/tests/src/unit-allocator.cpp index 399c8bfa8a..6f2c70148c 100644 --- a/tests/src/unit-allocator.cpp +++ b/tests/src/unit-allocator.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit-alt-string.cpp b/tests/src/unit-alt-string.cpp index 1d39e8db15..9c04afa4a5 100644 --- a/tests/src/unit-alt-string.cpp +++ b/tests/src/unit-alt-string.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-FileCopyrightText: 2018 Vitaliy Manushkin // SPDX-License-Identifier: MIT @@ -16,8 +16,8 @@ /* forward declarations */ class alt_string; -bool operator<(const char* op1, const alt_string& op2) noexcept; -void int_to_string(alt_string& target, std::size_t value); +bool operator<(const char* op1, const alt_string& op2) noexcept; // NOLINT(misc-use-internal-linkage) +void int_to_string(alt_string& target, std::size_t value); // NOLINT(misc-use-internal-linkage) /* * This is virtually a string class. @@ -28,17 +28,28 @@ class alt_string public: using value_type = std::string::value_type; - static constexpr auto npos = static_cast(-1); + static constexpr auto npos = (std::numeric_limits::max)(); alt_string(const char* str): str_impl(str) {} alt_string(const char* str, std::size_t count): str_impl(str, count) {} alt_string(size_t count, char chr): str_impl(count, chr) {} alt_string() = default; - template - alt_string& append(TParams&& ...params) + alt_string& append(char ch) { - str_impl.append(std::forward(params)...); + str_impl.push_back(ch); + return *this; + } + + alt_string& append(const alt_string& str) + { + str_impl.append(str.str_impl); + return *this; + } + + alt_string& append(const char* s, std::size_t length) + { + str_impl.append(s, length); return *this; } @@ -157,6 +168,11 @@ class alt_string return *this; } + void reserve( std::size_t new_cap = 0 ) + { + str_impl.reserve(new_cap); + } + private: std::string str_impl {}; // NOLINT(readability-redundant-member-init) @@ -191,49 +207,49 @@ TEST_CASE("alternative string type") { alt_json doc; doc["pi"] = 3.141; - alt_string dump = doc.dump(); + const alt_string dump = doc.dump(); CHECK(dump == R"({"pi":3.141})"); } { alt_json doc; doc["happy"] = true; - alt_string dump = doc.dump(); + const alt_string dump = doc.dump(); CHECK(dump == R"({"happy":true})"); } { alt_json doc; doc["name"] = "I'm Batman"; - alt_string dump = doc.dump(); + const alt_string dump = doc.dump(); CHECK(dump == R"({"name":"I'm Batman"})"); } { alt_json doc; doc["nothing"] = nullptr; - alt_string dump = doc.dump(); + const alt_string dump = doc.dump(); CHECK(dump == R"({"nothing":null})"); } { alt_json doc; doc["answer"]["everything"] = 42; - alt_string dump = doc.dump(); + const alt_string dump = doc.dump(); CHECK(dump == R"({"answer":{"everything":42}})"); } { alt_json doc; doc["list"] = { 1, 0, 2 }; - alt_string dump = doc.dump(); + const alt_string dump = doc.dump(); CHECK(dump == R"({"list":[1,0,2]})"); } { alt_json doc; doc["object"] = { {"currency", "USD"}, {"value", 42.99} }; - alt_string dump = doc.dump(); + const alt_string dump = doc.dump(); CHECK(dump == R"({"object":{"currency":"USD","value":42.99}})"); } } @@ -241,7 +257,7 @@ TEST_CASE("alternative string type") SECTION("parse") { auto doc = alt_json::parse(R"({"foo": "bar"})"); - alt_string dump = doc.dump(); + const alt_string dump = doc.dump(); CHECK(dump == R"({"foo":"bar"})"); } @@ -319,4 +335,36 @@ TEST_CASE("alternative string type") CHECK(j.at(alt_json::json_pointer("/foo/0")) == j["foo"][0]); CHECK(j.at(alt_json::json_pointer("/foo/1")) == j["foo"][1]); } + + SECTION("patch") + { + alt_json const patch1 = alt_json::parse(R"([{ "op": "add", "path": "/a/b", "value": [ "foo", "bar" ] }])"); + alt_json const doc1 = alt_json::parse(R"({ "a": { "foo": 1 } })"); + + CHECK_NOTHROW(doc1.patch(patch1)); + alt_json doc1_ans = alt_json::parse(R"( + { + "a": { + "foo": 1, + "b": [ "foo", "bar" ] + } + } + )"); + CHECK(doc1.patch(patch1) == doc1_ans); + } + + SECTION("diff") + { + alt_json const j1 = {"foo", "bar", "baz"}; + alt_json const j2 = {"foo", "bam"}; + CHECK(alt_json::diff(j1, j2).dump() == "[{\"op\":\"replace\",\"path\":\"/1\",\"value\":\"bam\"},{\"op\":\"remove\",\"path\":\"/2\"}]"); + } + + SECTION("flatten") + { + // a JSON value + const alt_json j = alt_json::parse(R"({"foo": ["bar", "baz"]})"); + const auto j2 = j.flatten(); + CHECK(j2.dump() == R"({"/foo/0":"bar","/foo/1":"baz"})"); + } } diff --git a/tests/src/unit-assert_macro.cpp b/tests/src/unit-assert_macro.cpp index 55eb3b9ec3..adedfc9344 100644 --- a/tests/src/unit-assert_macro.cpp +++ b/tests/src/unit-assert_macro.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit-binary_formats.cpp b/tests/src/unit-binary_formats.cpp index b41a6fec12..0c084b720a 100644 --- a/tests/src/unit-binary_formats.cpp +++ b/tests/src/unit-binary_formats.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit-bjdata.cpp b/tests/src/unit-bjdata.cpp index 9f247fe6fe..c8459315d1 100644 --- a/tests/src/unit-bjdata.cpp +++ b/tests/src/unit-bjdata.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -109,7 +109,7 @@ struct trait_test_arg static constexpr bool max_in_range = MaxInRange; }; -TEST_CASE_TEMPLATE_DEFINE("value_in_range_of trait", T, value_in_range_of_test) +TEST_CASE_TEMPLATE_DEFINE("value_in_range_of trait", T, value_in_range_of_test) // NOLINT(readability-math-missing-parentheses) { using nlohmann::detail::value_in_range_of; @@ -173,6 +173,7 @@ TEST_CASE_TEMPLATE_DEFINE("value_in_range_of trait", T, value_in_range_of_test) } } +// NOLINTNEXTLINE(bugprone-throwing-static-initialization) TEST_CASE_TEMPLATE_INVOKE(value_in_range_of_test, \ trait_test_arg, \ trait_test_arg, \ @@ -198,6 +199,7 @@ TEST_CASE_TEMPLATE_INVOKE(value_in_range_of_test, \ trait_test_arg, \ trait_test_arg); #else +// NOLINTNEXTLINE(bugprone-throwing-static-initialization) TEST_CASE_TEMPLATE_INVOKE(value_in_range_of_test, \ trait_test_arg, \ trait_test_arg, \ @@ -267,6 +269,34 @@ TEST_CASE("BJData") } } + SECTION("byte") + { + SECTION("0..255 (uint8)") + { + for (size_t i = 0; i <= 255; ++i) + { + CAPTURE(i) + + // create JSON value with integer number (no byte type in JSON) + json j = -1; + j.get_ref() = static_cast(i); + + // check type + CHECK(j.is_number_integer()); + + // create byte vector + std::vector const value + { + static_cast('B'), + static_cast(i), + }; + + // compare value + CHECK(json::from_bjdata(value) == j); + } + } + } + SECTION("number") { SECTION("signed") @@ -339,13 +369,13 @@ TEST_CASE("BJData") std::vector const numbers { -32769, - -100000, - -1000000, - -10000000, - -100000000, - -1000000000, - -2147483647 - 1, // https://stackoverflow.com/a/29356002/266378 - }; + -100000, + -1000000, + -10000000, + -100000000, + -1000000000, + -2147483647 - 1, // https://stackoverflow.com/a/29356002/266378 + }; for (const auto i : numbers) { CAPTURE(i) @@ -573,7 +603,7 @@ TEST_CASE("BJData") // check individual bytes CHECK(result[0] == 'I'); - auto const restored = static_cast(static_cast(result[2]) * 256 + static_cast(result[1])); + auto const restored = static_cast((static_cast(result[2]) * 256) + static_cast(result[1])); CHECK(restored == i); // roundtrip @@ -613,7 +643,7 @@ TEST_CASE("BJData") // check individual bytes CHECK(result[0] == 'u'); - auto const restored = static_cast(static_cast(result[2]) * 256 + static_cast(result[1])); + auto const restored = static_cast((static_cast(result[2]) * 256) + static_cast(result[1])); CHECK(restored == i); // roundtrip @@ -905,7 +935,7 @@ TEST_CASE("BJData") // check individual bytes CHECK(result[0] == 'I'); - auto const restored = static_cast(static_cast(result[2]) * 256 + static_cast(result[1])); + auto const restored = static_cast((static_cast(result[2]) * 256) + static_cast(result[1])); CHECK(restored == i); // roundtrip @@ -944,7 +974,7 @@ TEST_CASE("BJData") // check individual bytes CHECK(result[0] == 'u'); - auto const restored = static_cast(static_cast(result[2]) * 256 + static_cast(result[1])); + auto const restored = static_cast((static_cast(result[2]) * 256) + static_cast(result[1])); CHECK(restored == i); // roundtrip @@ -1198,21 +1228,21 @@ TEST_CASE("BJData") SECTION("0 (0 00000 0000000000)") { json const j = json::from_bjdata(std::vector({'h', 0x00, 0x00})); - json::number_float_t d{j}; + const json::number_float_t d{j}; CHECK(d == 0.0); } SECTION("-0 (1 00000 0000000000)") { json const j = json::from_bjdata(std::vector({'h', 0x00, 0x80})); - json::number_float_t d{j}; + const json::number_float_t d{j}; CHECK(d == -0.0); } SECTION("2**-24 (0 00000 0000000001)") { json const j = json::from_bjdata(std::vector({'h', 0x01, 0x00})); - json::number_float_t d{j}; + const json::number_float_t d{j}; CHECK(d == std::pow(2.0, -24.0)); } } @@ -1222,7 +1252,7 @@ TEST_CASE("BJData") SECTION("infinity (0 11111 0000000000)") { json const j = json::from_bjdata(std::vector({'h', 0x00, 0x7c})); - json::number_float_t d{j}; + const json::number_float_t d{j}; CHECK(d == std::numeric_limits::infinity()); CHECK(j.dump() == "null"); } @@ -1230,7 +1260,7 @@ TEST_CASE("BJData") SECTION("-infinity (1 11111 0000000000)") { json const j = json::from_bjdata(std::vector({'h', 0x00, 0xfc})); - json::number_float_t d{j}; + const json::number_float_t d{j}; CHECK(d == -std::numeric_limits::infinity()); CHECK(j.dump() == "null"); } @@ -1241,21 +1271,21 @@ TEST_CASE("BJData") SECTION("1 (0 01111 0000000000)") { json const j = json::from_bjdata(std::vector({'h', 0x00, 0x3c})); - json::number_float_t d{j}; + const json::number_float_t d{j}; CHECK(d == 1); } SECTION("-2 (1 10000 0000000000)") { json const j = json::from_bjdata(std::vector({'h', 0x00, 0xc0})); - json::number_float_t d{j}; + const json::number_float_t d{j}; CHECK(d == -2); } SECTION("65504 (0 11110 1111111111)") { json const j = json::from_bjdata(std::vector({'h', 0xff, 0x7b})); - json::number_float_t d{j}; + const json::number_float_t d{j}; CHECK(d == 65504); } } @@ -1501,262 +1531,316 @@ TEST_CASE("BJData") SECTION("binary") { - SECTION("N = 0..127") + for (json::bjdata_version_t bjdata_version : + { + json::bjdata_version_t::draft2, json::bjdata_version_t::draft3 + }) { - for (std::size_t N = 0; N <= 127; ++N) + CAPTURE(bjdata_version) + const bool draft3 = (bjdata_version == json::bjdata_version_t::draft3); + + SECTION("N = 0..127") { - CAPTURE(N) + for (std::size_t N = 0; N <= 127; ++N) + { + CAPTURE(N) - // create JSON value with byte array containing of N * 'x' - const auto s = std::vector(N, 'x'); - json const j = json::binary(s); + // create JSON value with byte array containing of N * 'x' + const auto s = std::vector(N, 'x'); + json const j = json::binary(s); - // create expected byte vector - std::vector expected; - expected.push_back(static_cast('[')); - if (N != 0) + // create expected byte vector + std::vector expected; + expected.push_back(static_cast('[')); + if (draft3 || N != 0) + { + expected.push_back(static_cast('$')); + expected.push_back(static_cast(draft3 ? 'B' : 'U')); + } + expected.push_back(static_cast('#')); + expected.push_back(static_cast('i')); + expected.push_back(static_cast(N)); + for (size_t i = 0; i < N; ++i) + { + expected.push_back(0x78); + } + + // compare result + size + const auto result = json::to_bjdata(j, true, true, bjdata_version); + CHECK(result == expected); + if (!draft3 && N == 0) + { + CHECK(result.size() == N + 4); + } + else + { + CHECK(result.size() == N + 6); + } + + // check that no null byte is appended + if (N > 0) + { + CHECK(result.back() != '\x00'); + } + + if (draft3) + { + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + else + { + // roundtrip only works to an array of numbers + json j_out = s; + CHECK(json::from_bjdata(result) == j_out); + CHECK(json::from_bjdata(result, true, false) == j_out); + } + } + } + + SECTION("N = 128..255") + { + for (std::size_t N = 128; N <= 255; ++N) { + CAPTURE(N) + + // create JSON value with byte array containing of N * 'x' + const auto s = std::vector(N, 'x'); + json const j = json::binary(s); + + // create expected byte vector + std::vector expected; + expected.push_back(static_cast('[')); expected.push_back(static_cast('$')); + expected.push_back(static_cast(draft3 ? 'B' : 'U')); + expected.push_back(static_cast('#')); expected.push_back(static_cast('U')); - } - expected.push_back(static_cast('#')); - expected.push_back(static_cast('i')); - expected.push_back(static_cast(N)); - for (size_t i = 0; i < N; ++i) - { - expected.push_back(0x78); - } + expected.push_back(static_cast(N)); + for (size_t i = 0; i < N; ++i) + { + expected.push_back(0x78); + } - // compare result + size - const auto result = json::to_bjdata(j, true, true); - CHECK(result == expected); - if (N == 0) - { - CHECK(result.size() == N + 4); - } - else - { + // compare result + size + const auto result = json::to_bjdata(j, true, true, bjdata_version); + CHECK(result == expected); CHECK(result.size() == N + 6); - } - - // check that no null byte is appended - if (N > 0) - { + // check that no null byte is appended CHECK(result.back() != '\x00'); - } - // roundtrip only works to an array of numbers - json j_out = s; - CHECK(json::from_bjdata(result) == j_out); - CHECK(json::from_bjdata(result, true, false) == j_out); + if (draft3) + { + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + else + { + // roundtrip only works to an array of numbers + json j_out = s; + CHECK(json::from_bjdata(result) == j_out); + CHECK(json::from_bjdata(result, true, false) == j_out); + } + } } - } - SECTION("N = 128..255") - { - for (std::size_t N = 128; N <= 255; ++N) + SECTION("N = 256..32767") { - CAPTURE(N) + for (const std::size_t N : + { + 256u, 999u, 1025u, 3333u, 2048u, 32767u + }) + { + CAPTURE(N) - // create JSON value with byte array containing of N * 'x' - const auto s = std::vector(N, 'x'); - json const j = json::binary(s); + // create JSON value with byte array containing of N * 'x' + const auto s = std::vector(N, 'x'); + json const j = json::binary(s); - // create expected byte vector - std::vector expected; - expected.push_back(static_cast('[')); - expected.push_back(static_cast('$')); - expected.push_back(static_cast('U')); - expected.push_back(static_cast('#')); - expected.push_back(static_cast('U')); - expected.push_back(static_cast(N)); - for (size_t i = 0; i < N; ++i) - { - expected.push_back(0x78); - } + // create expected byte vector + std::vector expected(N + 7, 'x'); + expected[0] = '['; + expected[1] = '$'; + expected[2] = draft3 ? 'B' : 'U'; + expected[3] = '#'; + expected[4] = 'I'; + expected[5] = static_cast(N & 0xFF); + expected[6] = static_cast((N >> 8) & 0xFF); - // compare result + size - const auto result = json::to_bjdata(j, true, true); - CHECK(result == expected); - CHECK(result.size() == N + 6); - // check that no null byte is appended - CHECK(result.back() != '\x00'); + // compare result + size + const auto result = json::to_bjdata(j, true, true, bjdata_version); + CHECK(result == expected); + CHECK(result.size() == N + 7); + // check that no null byte is appended + CHECK(result.back() != '\x00'); - // roundtrip only works to an array of numbers - json j_out = s; - CHECK(json::from_bjdata(result) == j_out); - CHECK(json::from_bjdata(result, true, false) == j_out); + if (draft3) + { + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + else + { + // roundtrip only works to an array of numbers + json j_out = s; + CHECK(json::from_bjdata(result) == j_out); + CHECK(json::from_bjdata(result, true, false) == j_out); + } + } } - } - SECTION("N = 256..32767") - { - for (const std::size_t N : - { - 256u, 999u, 1025u, 3333u, 2048u, 32767u - }) + SECTION("N = 32768..65535") { - CAPTURE(N) + for (const std::size_t N : + { + 32768u, 55555u, 65535u + }) + { + CAPTURE(N) - // create JSON value with byte array containing of N * 'x' - const auto s = std::vector(N, 'x'); - json const j = json::binary(s); + // create JSON value with byte array containing of N * 'x' + const auto s = std::vector(N, 'x'); + json const j = json::binary(s); - // create expected byte vector - std::vector expected(N + 7, 'x'); - expected[0] = '['; - expected[1] = '$'; - expected[2] = 'U'; - expected[3] = '#'; - expected[4] = 'I'; - expected[5] = static_cast(N & 0xFF); - expected[6] = static_cast((N >> 8) & 0xFF); + // create expected byte vector + std::vector expected(N + 7, 'x'); + expected[0] = '['; + expected[1] = '$'; + expected[2] = draft3 ? 'B' : 'U'; + expected[3] = '#'; + expected[4] = 'u'; + expected[5] = static_cast(N & 0xFF); + expected[6] = static_cast((N >> 8) & 0xFF); - // compare result + size - const auto result = json::to_bjdata(j, true, true); - CHECK(result == expected); - CHECK(result.size() == N + 7); - // check that no null byte is appended - CHECK(result.back() != '\x00'); + // compare result + size + const auto result = json::to_bjdata(j, true, true, bjdata_version); + CHECK(result == expected); + CHECK(result.size() == N + 7); + // check that no null byte is appended + CHECK(result.back() != '\x00'); - // roundtrip only works to an array of numbers - json j_out = s; - CHECK(json::from_bjdata(result) == j_out); - CHECK(json::from_bjdata(result, true, false) == j_out); + if (draft3) + { + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + else + { + // roundtrip only works to an array of numbers + json j_out = s; + CHECK(json::from_bjdata(result) == j_out); + CHECK(json::from_bjdata(result, true, false) == j_out); + } + } } - } - SECTION("N = 32768..65535") - { - for (const std::size_t N : - { - 32768u, 55555u, 65535u - }) + SECTION("N = 65536..2147483647") { - CAPTURE(N) + for (const std::size_t N : + { + 65536u, 77777u, 1048576u + }) + { + CAPTURE(N) - // create JSON value with byte array containing of N * 'x' - const auto s = std::vector(N, 'x'); - json const j = json::binary(s); + // create JSON value with byte array containing of N * 'x' + const auto s = std::vector(N, 'x'); + json const j = json::binary(s); - // create expected byte vector - std::vector expected(N + 7, 'x'); - expected[0] = '['; - expected[1] = '$'; - expected[2] = 'U'; - expected[3] = '#'; - expected[4] = 'u'; - expected[5] = static_cast(N & 0xFF); - expected[6] = static_cast((N >> 8) & 0xFF); + // create expected byte vector + std::vector expected(N + 9, 'x'); + expected[0] = '['; + expected[1] = '$'; + expected[2] = draft3 ? 'B' : 'U'; + expected[3] = '#'; + expected[4] = 'l'; + expected[5] = static_cast(N & 0xFF); + expected[6] = static_cast((N >> 8) & 0xFF); + expected[7] = static_cast((N >> 16) & 0xFF); + expected[8] = static_cast((N >> 24) & 0xFF); - // compare result + size - const auto result = json::to_bjdata(j, true, true); - CHECK(result == expected); - CHECK(result.size() == N + 7); - // check that no null byte is appended - CHECK(result.back() != '\x00'); + // compare result + size + const auto result = json::to_bjdata(j, true, true, bjdata_version); + CHECK(result == expected); + CHECK(result.size() == N + 9); + // check that no null byte is appended + CHECK(result.back() != '\x00'); - // roundtrip only works to an array of numbers - json j_out = s; - CHECK(json::from_bjdata(result) == j_out); - CHECK(json::from_bjdata(result, true, false) == j_out); + if (draft3) + { + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + else + { + // roundtrip only works to an array of numbers + json j_out = s; + CHECK(json::from_bjdata(result) == j_out); + CHECK(json::from_bjdata(result, true, false) == j_out); + } + } } - } - SECTION("N = 65536..2147483647") - { - for (const std::size_t N : - { - 65536u, 77777u, 1048576u - }) + SECTION("Other Serializations") { - CAPTURE(N) - - // create JSON value with byte array containing of N * 'x' + const std::size_t N = 10; const auto s = std::vector(N, 'x'); json const j = json::binary(s); - // create expected byte vector - std::vector expected(N + 9, 'x'); - expected[0] = '['; - expected[1] = '$'; - expected[2] = 'U'; - expected[3] = '#'; - expected[4] = 'l'; - expected[5] = static_cast(N & 0xFF); - expected[6] = static_cast((N >> 8) & 0xFF); - expected[7] = static_cast((N >> 16) & 0xFF); - expected[8] = static_cast((N >> 24) & 0xFF); - - // compare result + size - const auto result = json::to_bjdata(j, true, true); - CHECK(result == expected); - CHECK(result.size() == N + 9); - // check that no null byte is appended - CHECK(result.back() != '\x00'); - - // roundtrip only works to an array of numbers - json j_out = s; - CHECK(json::from_bjdata(result) == j_out); - CHECK(json::from_bjdata(result, true, false) == j_out); - } - } + SECTION("No Count No Type") + { + std::vector expected; + expected.push_back(static_cast('[')); + for (std::size_t i = 0; i < N; ++i) + { + expected.push_back(static_cast(draft3 ? 'B' : 'U')); + expected.push_back(static_cast(0x78)); + } + expected.push_back(static_cast(']')); - SECTION("Other Serializations") - { - const std::size_t N = 10; - const auto s = std::vector(N, 'x'); - json const j = json::binary(s); + // compare result + size + const auto result = json::to_bjdata(j, false, false, bjdata_version); + CHECK(result == expected); + CHECK(result.size() == N + 12); + // check that no null byte is appended + CHECK(result.back() != '\x00'); - SECTION("No Count No Type") - { - std::vector expected; - expected.push_back(static_cast('[')); - for (std::size_t i = 0; i < N; ++i) - { - expected.push_back(static_cast('U')); - expected.push_back(static_cast(0x78)); + // roundtrip only works to an array of numbers + json j_out = s; + CHECK(json::from_bjdata(result) == j_out); + CHECK(json::from_bjdata(result, true, false) == j_out); } - expected.push_back(static_cast(']')); - // compare result + size - const auto result = json::to_bjdata(j, false, false); - CHECK(result == expected); - CHECK(result.size() == N + 12); - // check that no null byte is appended - CHECK(result.back() != '\x00'); + SECTION("Yes Count No Type") + { + std::vector expected; + expected.push_back(static_cast('[')); + expected.push_back(static_cast('#')); + expected.push_back(static_cast('i')); + expected.push_back(static_cast(N)); - // roundtrip only works to an array of numbers - json j_out = s; - CHECK(json::from_bjdata(result) == j_out); - CHECK(json::from_bjdata(result, true, false) == j_out); - } + for (size_t i = 0; i < N; ++i) + { + expected.push_back(static_cast(draft3 ? 'B' : 'U')); + expected.push_back(static_cast(0x78)); + } - SECTION("Yes Count No Type") - { - std::vector expected; - expected.push_back(static_cast('[')); - expected.push_back(static_cast('#')); - expected.push_back(static_cast('i')); - expected.push_back(static_cast(N)); + // compare result + size + const auto result = json::to_bjdata(j, true, false, bjdata_version); + CHECK(result == expected); + CHECK(result.size() == N + 14); + // check that no null byte is appended + CHECK(result.back() != '\x00'); - for (size_t i = 0; i < N; ++i) - { - expected.push_back(static_cast('U')); - expected.push_back(static_cast(0x78)); + // roundtrip only works to an array of numbers + json j_out = s; + CHECK(json::from_bjdata(result) == j_out); + CHECK(json::from_bjdata(result, true, false) == j_out); } - - // compare result + size - const auto result = json::to_bjdata(j, true, false); - CHECK(result == expected); - CHECK(result.size() == N + 14); - // check that no null byte is appended - CHECK(result.back() != '\x00'); - - // roundtrip only works to an array of numbers - json j_out = s; - CHECK(json::from_bjdata(result) == j_out); - CHECK(json::from_bjdata(result, true, false) == j_out); } } } @@ -2334,6 +2418,7 @@ TEST_CASE("BJData") std::vector const v_D = {'[', '#', 'i', 2, 'D', 0x4a, 0xd8, 0x12, 0x4d, 0xfb, 0x21, 0x09, 0x40, 'D', 0x4a, 0xd8, 0x12, 0x4d, 0xfb, 0x21, 0x09, 0x40}; std::vector const v_S = {'[', '#', 'i', 2, 'S', 'i', 1, 'a', 'S', 'i', 1, 'a'}; std::vector const v_C = {'[', '#', 'i', 2, 'C', 'a', 'C', 'a'}; + std::vector const v_B = {'[', '#', 'i', 2, 'B', 0xFF, 'B', 0xFF}; // check if vector is parsed correctly CHECK(json::from_bjdata(v_TU) == json({true, true})); @@ -2351,6 +2436,7 @@ TEST_CASE("BJData") CHECK(json::from_bjdata(v_D) == json({3.1415926, 3.1415926})); CHECK(json::from_bjdata(v_S) == json({"a", "a"})); CHECK(json::from_bjdata(v_C) == json({"a", "a"})); + CHECK(json::from_bjdata(v_B) == json({255, 255})); // roundtrip: output should be optimized CHECK(json::to_bjdata(json::from_bjdata(v_T), true) == v_T); @@ -2367,6 +2453,7 @@ TEST_CASE("BJData") CHECK(json::to_bjdata(json::from_bjdata(v_D), true) == v_D); CHECK(json::to_bjdata(json::from_bjdata(v_S), true) == v_S); CHECK(json::to_bjdata(json::from_bjdata(v_C), true) == v_S); // char is serialized to string + CHECK(json::to_bjdata(json::from_bjdata(v_B), true) == v_U); // byte is serialized to uint8 } SECTION("optimized version (type and length)") @@ -2383,6 +2470,7 @@ TEST_CASE("BJData") std::vector const v_D = {'[', '$', 'D', '#', 'i', 2, 0x4a, 0xd8, 0x12, 0x4d, 0xfb, 0x21, 0x09, 0x40, 0x4a, 0xd8, 0x12, 0x4d, 0xfb, 0x21, 0x09, 0x40}; std::vector const v_S = {'[', '#', 'i', 2, 'S', 'i', 1, 'a', 'S', 'i', 1, 'a'}; std::vector const v_C = {'[', '$', 'C', '#', 'i', 2, 'a', 'a'}; + std::vector const v_B = {'[', '$', 'B', '#', 'i', 2, 0xFF, 0xFF}; // check if vector is parsed correctly CHECK(json::from_bjdata(v_i) == json({127, 127})); @@ -2396,6 +2484,7 @@ TEST_CASE("BJData") CHECK(json::from_bjdata(v_D) == json({3.1415926, 3.1415926})); CHECK(json::from_bjdata(v_S) == json({"a", "a"})); CHECK(json::from_bjdata(v_C) == json({"a", "a"})); + CHECK(json::from_bjdata(v_B) == json::binary(std::vector({static_cast(255), static_cast(255)}))); // roundtrip: output should be optimized std::vector const v_empty = {'[', '#', 'i', 0}; @@ -2410,6 +2499,8 @@ TEST_CASE("BJData") CHECK(json::to_bjdata(json::from_bjdata(v_D), true, true) == v_D); CHECK(json::to_bjdata(json::from_bjdata(v_S), true, true) == v_S); CHECK(json::to_bjdata(json::from_bjdata(v_C), true, true) == v_S); // char is serialized to string + CHECK(json::to_bjdata(json::from_bjdata(v_B), true, true, json::bjdata_version_t::draft2) == v_U); + CHECK(json::to_bjdata(json::from_bjdata(v_B), true, true, json::bjdata_version_t::draft3) == v_B); } SECTION("optimized ndarray (type and vector-size as optimized 1D array)") @@ -2428,6 +2519,7 @@ TEST_CASE("BJData") std::vector const v_D = {'[', '$', 'D', '#', '[', '$', 'i', '#', 'i', 2, 1, 2, 0x4a, 0xd8, 0x12, 0x4d, 0xfb, 0x21, 0x09, 0x40, 0x4a, 0xd8, 0x12, 0x4d, 0xfb, 0x21, 0x09, 0x40}; std::vector const v_S = {'[', '#', '[', '$', 'i', '#', 'i', 2, 1, 2, 'S', 'i', 1, 'a', 'S', 'i', 1, 'a'}; std::vector const v_C = {'[', '$', 'C', '#', '[', '$', 'i', '#', 'i', 2, 1, 2, 'a', 'a'}; + std::vector const v_B = {'[', '$', 'B', '#', '[', '$', 'i', '#', 'i', 2, 1, 2, 0xFF, 0xFF}; // check if vector is parsed correctly CHECK(json::from_bjdata(v_0) == json::array()); @@ -2443,6 +2535,7 @@ TEST_CASE("BJData") CHECK(json::from_bjdata(v_D) == json({3.1415926, 3.1415926})); CHECK(json::from_bjdata(v_S) == json({"a", "a"})); CHECK(json::from_bjdata(v_C) == json({"a", "a"})); + CHECK(json::from_bjdata(v_B) == json::binary(std::vector({static_cast(255), static_cast(255)}))); } SECTION("optimized ndarray (type and vector-size ndarray with JData annotations)") @@ -2460,6 +2553,7 @@ TEST_CASE("BJData") std::vector const v_d = {'[', '$', 'd', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0xA0, 0x40, 0x00, 0x00, 0xC0, 0x40}; std::vector const v_D = {'[', '$', 'D', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x40}; std::vector const v_C = {'[', '$', 'C', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 'a', 'b', 'c', 'd', 'e', 'f'}; + std::vector const v_B = {'[', '$', 'B', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; // check if vector is parsed correctly CHECK(json::from_bjdata(v_e) == json({{"_ArrayData_", {254, 255}}, {"_ArraySize_", {2, 1}}, {"_ArrayType_", "uint8"}})); @@ -2475,6 +2569,7 @@ TEST_CASE("BJData") CHECK(json::from_bjdata(v_d) == json({{"_ArrayData_", {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "single"}})); CHECK(json::from_bjdata(v_D) == json({{"_ArrayData_", {1., 2., 3., 4., 5., 6.}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "double"}})); CHECK(json::from_bjdata(v_C) == json({{"_ArrayData_", {'a', 'b', 'c', 'd', 'e', 'f'}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "char"}})); + CHECK(json::from_bjdata(v_B) == json({{"_ArrayData_", {1, 2, 3, 4, 5, 6}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "byte"}})); // roundtrip: output should be optimized CHECK(json::to_bjdata(json::from_bjdata(v_e), true, true) == v_e); @@ -2489,6 +2584,7 @@ TEST_CASE("BJData") CHECK(json::to_bjdata(json::from_bjdata(v_d), true, true) == v_d); CHECK(json::to_bjdata(json::from_bjdata(v_D), true, true) == v_D); CHECK(json::to_bjdata(json::from_bjdata(v_C), true, true) == v_C); + CHECK(json::to_bjdata(json::from_bjdata(v_B), true, true) == v_B); } SECTION("optimized ndarray (type and vector-size as 1D array)") @@ -2507,6 +2603,7 @@ TEST_CASE("BJData") std::vector const v_D = {'[', '$', 'D', '#', '[', 'i', 1, 'i', 2, ']', 0x4a, 0xd8, 0x12, 0x4d, 0xfb, 0x21, 0x09, 0x40, 0x4a, 0xd8, 0x12, 0x4d, 0xfb, 0x21, 0x09, 0x40}; std::vector const v_S = {'[', '#', '[', 'i', 1, 'i', 2, ']', 'S', 'i', 1, 'a', 'S', 'i', 1, 'a'}; std::vector const v_C = {'[', '$', 'C', '#', '[', 'i', 1, 'i', 2, ']', 'a', 'a'}; + std::vector const v_B = {'[', '$', 'B', '#', '[', 'i', 1, 'i', 2, ']', 0xFF, 0xFF}; std::vector const v_R = {'[', '#', '[', 'i', 2, ']', 'i', 6, 'U', 7}; // check if vector is parsed correctly @@ -2523,6 +2620,7 @@ TEST_CASE("BJData") CHECK(json::from_bjdata(v_D) == json({3.1415926, 3.1415926})); CHECK(json::from_bjdata(v_S) == json({"a", "a"})); CHECK(json::from_bjdata(v_C) == json({"a", "a"})); + CHECK(json::from_bjdata(v_B) == json::binary(std::vector({static_cast(255), static_cast(255)}))); CHECK(json::from_bjdata(v_R) == json({6, 7})); } @@ -2540,6 +2638,7 @@ TEST_CASE("BJData") std::vector const v_D = {'[', '$', 'D', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2, 0x4a, 0xd8, 0x12, 0x4d, 0xfb, 0x21, 0x09, 0x40, 0x4a, 0xd8, 0x12, 0x4d, 0xfb, 0x21, 0x09, 0x40}; std::vector const v_S = {'[', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2, 'S', 'i', 1, 'a', 'S', 'i', 1, 'a'}; std::vector const v_C = {'[', '$', 'C', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2, 'a', 'a'}; + std::vector const v_B = {'[', '$', 'B', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2, 0xFF, 0xFF}; // check if vector is parsed correctly CHECK(json::from_bjdata(v_i) == json({127, 127})); @@ -2553,6 +2652,7 @@ TEST_CASE("BJData") CHECK(json::from_bjdata(v_D) == json({3.1415926, 3.1415926})); CHECK(json::from_bjdata(v_S) == json({"a", "a"})); CHECK(json::from_bjdata(v_C) == json({"a", "a"})); + CHECK(json::from_bjdata(v_B) == json::binary(std::vector({static_cast(255), static_cast(255)}))); } SECTION("invalid ndarray annotations remains as object") @@ -2594,6 +2694,17 @@ TEST_CASE("BJData") } } + SECTION("byte") + { + SECTION("parse bjdata markers in ubjson") + { + std::vector const v = {'B', 1}; + + json _; + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(v), "[json.exception.parse_error.112] parse error at byte 1: syntax error while parsing UBJSON value: invalid byte: 0x42", json::parse_error&); + } + } + SECTION("strings") { SECTION("eof after S byte") @@ -2706,6 +2817,129 @@ TEST_CASE("BJData") #endif } + SECTION("overflow detection in dimension multiplication") + { + // Simple SAX handler just to monitor if overflow is detected + struct SimpleOverflowSaxHandler : public nlohmann::json_sax + { + bool overflow_detected = false; + + // Implement all required virtual methods with minimal implementation + bool null() override + { + return true; + } + bool boolean(bool /*val*/) override + { + return true; + } + bool number_integer(json::number_integer_t /*val*/) override + { + return true; + } + bool number_unsigned(json::number_unsigned_t /*val*/) override + { + return true; + } + bool number_float(json::number_float_t /*val*/, const std::string& /*s*/) override + { + return true; + } + bool string(std::string& /*val*/) override + { + return true; + } + bool binary(json::binary_t& /*val*/) override + { + return true; + } + bool start_object(std::size_t /*elements*/) override + { + return true; + } + bool key(std::string& /*val*/) override + { + return true; + } + bool end_object() override + { + return true; + } + bool start_array(std::size_t /*elements*/) override + { + return true; + } + bool end_array() override + { + return true; + } + + // This is the only method we care about - detecting error 408 + bool parse_error(std::size_t /*position*/, const std::string& /*last_token*/, const json::exception& ex) override + { + if (ex.id == 408) + { + overflow_detected = true; + } + return false; + } + }; + + // Create BJData payload with overflow-causing dimensions (2^32+1) × (2^32) + const std::vector bjdata_payload = + { + 0x5B, // '[' start array + 0x24, 0x55, // '$', 'U' (type uint8) + 0x23, 0x5B, // '#', '[' (dimensions array) + 0x4D, // 'M' (uint64) + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 2^32 + 1 (4294967297) as little-endian + 0x4D, // 'M' (uint64) + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 2^32 (4294967296) as little-endian + 0x5D // ']' end dimensions + // No data - we don't need it for this test, we just want to hit the overflow check + }; + + // Test with overflow dimensions using SAX parser + { + SimpleOverflowSaxHandler handler; + const auto result = json::sax_parse(bjdata_payload, &handler, + nlohmann::detail::input_format_t::bjdata, false); + + // Should detect overflow + CHECK(handler.overflow_detected == true); + CHECK(result == false); + } + + // Test with DOM parser (should throw) + { + json _; + CHECK_THROWS_AS(_ = json::from_bjdata(bjdata_payload), json::out_of_range); + } + + // Test with normal dimensions + const std::vector normal_payload = + { + 0x5B, // '[' start array + 0x24, 0x55, // '$', 'U' (type uint8) + 0x23, 0x5B, // '#', '[' (dimensions array) + 0x55, 0x02, // 'U', 2 (uint8) + 0x55, 0x03, // 'U', 3 (uint8) + 0x5D, // ']' end dimensions + // 6 data bytes for a 2×3 array (enough to avoid EOF but not entire array) + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 + }; + + // For normal dimensions, overflow should not be detected + { + SimpleOverflowSaxHandler handler; + const auto result = json::sax_parse(normal_payload, &handler, + nlohmann::detail::input_format_t::bjdata, false); + + CHECK(handler.overflow_detected == false); + CHECK(result == true); + } + } + SECTION("do not accept NTFZ markers in ndarray optimized type (with count)") { json _; @@ -2803,6 +3037,10 @@ TEST_CASE("BJData") std::vector const v0 = {'[', '#', 'T', ']'}; CHECK_THROWS_WITH(_ = json::from_bjdata(v0), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing BJData size: expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x54"); CHECK(json::from_bjdata(v0, true, false).is_discarded()); + + std::vector const vB = {'[', '#', 'B', ']'}; + CHECK_THROWS_WITH(_ = json::from_bjdata(vB), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing BJData size: expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x42"); + CHECK(json::from_bjdata(v0, true, false).is_discarded()); } SECTION("parse bjdata markers as array size in ubjson") @@ -2904,6 +3142,10 @@ TEST_CASE("BJData") CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vU), "[json.exception.parse_error.110] parse error at byte 18: syntax error while parsing BJData number: unexpected end of input", json::parse_error&); CHECK(json::from_bjdata(vU, true, false).is_discarded()); + std::vector const vB = {'[', '$', 'B', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 1, 2, 3, 4, 5}; + CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vU), "[json.exception.parse_error.110] parse error at byte 18: syntax error while parsing BJData number: unexpected end of input", json::parse_error&); + CHECK(json::from_bjdata(vU, true, false).is_discarded()); + std::vector const vT1 = {'[', '$', 'T', '#', '[', '$', 'i', '#', 'i', 2, 2, 3}; CHECK(json::from_bjdata(vT1, true, false).is_discarded()); @@ -3197,6 +3439,21 @@ TEST_CASE("Universal Binary JSON Specification Examples 1") CHECK(json::from_bjdata(v) == j); } + SECTION("Byte Type") + { + const auto s = std::vector( + { + static_cast(222), + static_cast(173), + static_cast(190), + static_cast(239) + }); + json const j = {{"binary", json::binary(s)}, {"val", 123}}; + std::vector const v = {'{', 'i', 6, 'b', 'i', 'n', 'a', 'r', 'y', '[', '$', 'B', '#', 'i', 4, 222, 173, 190, 239, 'i', 3, 'v', 'a', 'l', 'i', 123, '}'}; + //CHECK(json::to_bjdata(j) == v); // 123 value gets encoded as uint8 + CHECK(json::from_bjdata(v) == j); + } + SECTION("String Type") { SECTION("English") @@ -3448,7 +3705,7 @@ TEST_CASE("all BJData first bytes") // these bytes will fail immediately with exception parse_error.112 std::set supported = { - 'T', 'F', 'Z', 'U', 'i', 'I', 'l', 'L', 'd', 'D', 'C', 'S', '[', '{', 'N', 'H', 'u', 'm', 'M', 'h' + 'T', 'F', 'Z', 'B', 'U', 'i', 'I', 'l', 'L', 'd', 'D', 'C', 'S', '[', '{', 'N', 'H', 'u', 'm', 'M', 'h' }; for (auto i = 0; i < 256; ++i) @@ -3534,7 +3791,7 @@ TEST_CASE("BJData roundtrips" * doctest::skip()) INFO_WITH_TEMP(filename + ": std::vector"); // parse JSON file std::ifstream f_json(filename); - json j1 = json::parse(f_json); + const json j1 = json::parse(f_json); // parse BJData file auto packed = utils::read_binary_file(filename + ".bjdata"); @@ -3549,7 +3806,7 @@ TEST_CASE("BJData roundtrips" * doctest::skip()) INFO_WITH_TEMP(filename + ": std::ifstream"); // parse JSON file std::ifstream f_json(filename); - json j1 = json::parse(f_json); + const json j1 = json::parse(f_json); // parse BJData file std::ifstream f_bjdata(filename + ".bjdata", std::ios::binary); diff --git a/tests/src/unit-bson.cpp b/tests/src/unit-bson.cpp index 13216f2f5a..eed70352e8 100644 --- a/tests/src/unit-bson.cpp +++ b/tests/src/unit-bson.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -331,7 +331,6 @@ TEST_CASE("BSON") SECTION("non-empty object with unsigned integer (64-bit) member") { - // directly encoding uint64 is not supported in bson (only for timestamp values) json const j = { { "entry", std::uint64_t{0x1234567804030201} } @@ -531,7 +530,6 @@ TEST_CASE("BSON") SECTION("Some more complex document") { - // directly encoding uint64 is not supported in bson (only for timestamp values) json const j = { {"double", 42.5}, @@ -567,10 +565,10 @@ TEST_CASE("BSON") SECTION("Example 1") { std::vector input = {0x16, 0x00, 0x00, 0x00, 0x02, 'h', 'e', 'l', 'l', 'o', 0x00, 0x06, 0x00, 0x00, 0x00, 'w', 'o', 'r', 'l', 'd', 0x00, 0x00}; - json parsed = json::from_bson(input); - json expected = {{"hello", "world"}}; + const json parsed = json::from_bson(input); + const json expected = {{"hello", "world"}}; CHECK(parsed == expected); - auto dumped = json::to_bson(parsed); + const auto dumped = json::to_bson(parsed); CHECK(dumped == input); CHECK(json::from_bson(dumped) == expected); } @@ -578,10 +576,10 @@ TEST_CASE("BSON") SECTION("Example 2") { std::vector input = {0x31, 0x00, 0x00, 0x00, 0x04, 'B', 'S', 'O', 'N', 0x00, 0x26, 0x00, 0x00, 0x00, 0x02, 0x30, 0x00, 0x08, 0x00, 0x00, 0x00, 'a', 'w', 'e', 's', 'o', 'm', 'e', 0x00, 0x01, 0x31, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x14, 0x40, 0x10, 0x32, 0x00, 0xc2, 0x07, 0x00, 0x00, 0x00, 0x00}; - json parsed = json::from_bson(input); - json expected = {{"BSON", {"awesome", 5.05, 1986}}}; + const json parsed = json::from_bson(input); + const json expected = {{"BSON", {"awesome", 5.05, 1986}}}; CHECK(parsed == expected); - auto dumped = json::to_bson(parsed); + const auto dumped = json::to_bson(parsed); CHECK(dumped == input); CHECK(json::from_bson(dumped) == expected); } @@ -590,7 +588,7 @@ TEST_CASE("BSON") TEST_CASE("BSON input/output_adapters") { - json json_representation = + const json json_representation = { {"double", 42.5}, {"entry", 4.2}, @@ -598,7 +596,7 @@ TEST_CASE("BSON input/output_adapters") {"object", {{ "string", "value" }}} }; - std::vector const bson_representation = + const std::vector bson_representation = { /*size */ 0x4f, 0x00, 0x00, 0x00, /*entry*/ 0x01, 'd', 'o', 'u', 'b', 'l', 'e', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x45, 0x40, @@ -621,9 +619,9 @@ TEST_CASE("BSON input/output_adapters") { SECTION("std::ostringstream") { - std::basic_ostringstream ss; + std::basic_ostringstream ss; json::to_bson(json_representation, ss); - json j3 = json::from_bson(ss.str()); + const json j3 = json::from_bson(ss.str()); CHECK(json_representation == j3); } @@ -631,7 +629,7 @@ TEST_CASE("BSON input/output_adapters") { std::string s; json::to_bson(json_representation, s); - json j3 = json::from_bson(s); + const json j3 = json::from_bson(s); CHECK(json_representation == j3); } @@ -639,7 +637,7 @@ TEST_CASE("BSON input/output_adapters") { std::vector v; json::to_bson(json_representation, v); - json j3 = json::from_bson(v); + const json j3 = json::from_bson(v); CHECK(json_representation == j3); } } @@ -1164,10 +1162,7 @@ TEST_CASE("BSON numerical data") std::vector const numbers { static_cast((std::numeric_limits::max)()) + 1ULL, - 10000000000000000000ULL, - 18000000000000000000ULL, - (std::numeric_limits::max)() - 1ULL, - (std::numeric_limits::max)(), + 0xffffffffffffffff, }; for (const auto i : numbers) @@ -1184,7 +1179,7 @@ TEST_CASE("BSON numerical data") std::vector const expected_bson = { 0x14u, 0x00u, 0x00u, 0x00u, // size (little endian) - 0x12u, /// entry: int64 + 0x11u, /// entry: uint64 'e', 'n', 't', 'r', 'y', '\x00', static_cast((iu >> (8u * 0u)) & 0xffu), static_cast((iu >> (8u * 1u)) & 0xffu), @@ -1197,12 +1192,15 @@ TEST_CASE("BSON numerical data") 0x00u // end marker }; - CHECK_THROWS_AS(json::to_bson(j), json::out_of_range&); -#if JSON_DIAGNOSTICS - CHECK_THROWS_WITH_STD_STR(json::to_bson(j), "[json.exception.out_of_range.407] (/entry) integer number " + std::to_string(i) + " cannot be represented by BSON as it does not fit int64"); -#else - CHECK_THROWS_WITH_STD_STR(json::to_bson(j), "[json.exception.out_of_range.407] integer number " + std::to_string(i) + " cannot be represented by BSON as it does not fit int64"); -#endif + const auto bson = json::to_bson(j); + CHECK(bson == expected_bson); + + auto j_roundtrip = json::from_bson(bson); + + CHECK(j.at("entry").is_number_unsigned()); + CHECK(j_roundtrip.at("entry").is_number_unsigned()); + CHECK(j_roundtrip == j); + CHECK(json::from_bson(bson, true, false) == j); } } @@ -1229,7 +1227,7 @@ TEST_CASE("BSON roundtrips" * doctest::skip()) INFO_WITH_TEMP(filename + ": std::vector"); // parse JSON file std::ifstream f_json(filename); - json j1 = json::parse(f_json); + const json j1 = json::parse(f_json); // parse BSON file auto packed = utils::read_binary_file(filename + ".bson"); @@ -1244,7 +1242,7 @@ TEST_CASE("BSON roundtrips" * doctest::skip()) INFO_WITH_TEMP(filename + ": std::ifstream"); // parse JSON file std::ifstream f_json(filename); - json j1 = json::parse(f_json); + const json j1 = json::parse(f_json); // parse BSON file std::ifstream f_bson(filename + ".bson", std::ios::binary); @@ -1259,7 +1257,7 @@ TEST_CASE("BSON roundtrips" * doctest::skip()) INFO_WITH_TEMP(filename + ": uint8_t* and size"); // parse JSON file std::ifstream f_json(filename); - json j1 = json::parse(f_json); + const json j1 = json::parse(f_json); // parse BSON file auto packed = utils::read_binary_file(filename + ".bson"); diff --git a/tests/src/unit-byte_container_with_subtype.cpp b/tests/src/unit-byte_container_with_subtype.cpp index 4116076b00..3983ba95f0 100644 --- a/tests/src/unit-byte_container_with_subtype.cpp +++ b/tests/src/unit-byte_container_with_subtype.cpp @@ -1,10 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// Copyright (c) 2013-2022 Niels Lohmann . -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit-capacity.cpp b/tests/src/unit-capacity.cpp index 61203cc231..98fcd00892 100644 --- a/tests/src/unit-capacity.cpp +++ b/tests/src/unit-capacity.cpp @@ -1,10 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// Copyright (c) 2013-2022 Niels Lohmann . -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit-cbor.cpp b/tests/src/unit-cbor.cpp index be94d2f6e1..34c259757a 100644 --- a/tests/src/unit-cbor.cpp +++ b/tests/src/unit-cbor.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -14,7 +14,6 @@ using nlohmann::json; #include #include #include -#include #include #include #include "make_test_data_available.hpp" @@ -241,13 +240,13 @@ TEST_CASE("CBOR") const std::vector numbers { -65537, - -100000, - -1000000, - -10000000, - -100000000, - -1000000000, - -4294967296, - }; + -100000, + -1000000, + -10000000, + -100000000, + -1000000000, + -4294967296, + }; for (const auto i : numbers) { CAPTURE(i) @@ -317,7 +316,7 @@ TEST_CASE("CBOR") // check individual bytes CHECK(result[0] == 0x39); - const auto restored = static_cast(static_cast(result[1]) * 256 + static_cast(result[2])); + const auto restored = static_cast((static_cast(result[1]) * 256) + static_cast(result[2])); CHECK(restored == positive); CHECK(-1 - restored == i); @@ -505,7 +504,7 @@ TEST_CASE("CBOR") // check individual bytes CHECK(result[0] == 0x19); - const auto restored = static_cast(static_cast(result[1]) * 256 + static_cast(result[2])); + const auto restored = static_cast((static_cast(result[1]) * 256) + static_cast(result[2])); CHECK(restored == i); // roundtrip @@ -744,7 +743,7 @@ TEST_CASE("CBOR") // check individual bytes CHECK(result[0] == 0x19); - const auto restored = static_cast(static_cast(result[1]) * 256 + static_cast(result[2])); + const auto restored = static_cast((static_cast(result[1]) * 256) + static_cast(result[2])); CHECK(restored == i); // roundtrip @@ -990,7 +989,7 @@ TEST_CASE("CBOR") { 0xfa, 0xff, 0x7f, 0xff, 0xff }; - // the same with lowest float + // the same with the lowest float const auto result = json::to_cbor(j); CHECK(result == expected); // roundtrip @@ -1023,21 +1022,21 @@ TEST_CASE("CBOR") SECTION("0 (0 00000 0000000000)") { json const j = json::from_cbor(std::vector({0xf9, 0x00, 0x00})); - json::number_float_t d{j}; + const json::number_float_t d{j}; CHECK(d == 0.0); } SECTION("-0 (1 00000 0000000000)") { json const j = json::from_cbor(std::vector({0xf9, 0x80, 0x00})); - json::number_float_t d{j}; + const json::number_float_t d{j}; CHECK(d == -0.0); } SECTION("2**-24 (0 00000 0000000001)") { json const j = json::from_cbor(std::vector({0xf9, 0x00, 0x01})); - json::number_float_t d{j}; + const json::number_float_t d{j}; CHECK(d == std::pow(2.0, -24.0)); } } @@ -1047,7 +1046,7 @@ TEST_CASE("CBOR") SECTION("infinity (0 11111 0000000000)") { json const j = json::from_cbor(std::vector({0xf9, 0x7c, 0x00})); - json::number_float_t d{j}; + const json::number_float_t d{j}; CHECK(d == std::numeric_limits::infinity()); CHECK(j.dump() == "null"); } @@ -1055,7 +1054,7 @@ TEST_CASE("CBOR") SECTION("-infinity (1 11111 0000000000)") { json const j = json::from_cbor(std::vector({0xf9, 0xfc, 0x00})); - json::number_float_t d{j}; + const json::number_float_t d{j}; CHECK(d == -std::numeric_limits::infinity()); CHECK(j.dump() == "null"); } @@ -1066,21 +1065,21 @@ TEST_CASE("CBOR") SECTION("1 (0 01111 0000000000)") { json const j = json::from_cbor(std::vector({0xf9, 0x3c, 0x00})); - json::number_float_t d{j}; + const json::number_float_t d{j}; CHECK(d == 1); } SECTION("-2 (1 10000 0000000000)") { json const j = json::from_cbor(std::vector({0xf9, 0xc0, 0x00})); - json::number_float_t d{j}; + const json::number_float_t d{j}; CHECK(d == -2); } SECTION("65504 (0 11110 1111111111)") { json const j = json::from_cbor(std::vector({0xf9, 0x7b, 0xff})); - json::number_float_t d{j}; + const json::number_float_t d{j}; CHECK(d == 65504); } } @@ -1632,7 +1631,7 @@ TEST_CASE("CBOR") }; json j; - auto cbp = nlohmann::detail::json_sax_dom_callback_parser(j, callback, true); + auto cbp = nlohmann::detail::json_sax_dom_callback_parser(j, callback, true); CHECK(json::sax_parse(input, &cbp, json::input_format_t::cbor)); CHECK(j.at("foo").is_binary()); CHECK(binary_seen); @@ -1881,7 +1880,7 @@ TEST_CASE("single CBOR roundtrip") { SECTION("std::ostringstream") { - std::basic_ostringstream ss; + std::basic_ostringstream ss; json::to_cbor(j1, ss); json j3 = json::from_cbor(ss.str()); CHECK(j1 == j3); @@ -1943,7 +1942,7 @@ TEST_CASE("CBOR regressions") { // parse CBOR file auto vec1 = utils::read_binary_file(filename); - json j1 = json::from_cbor(vec1); + const json j1 = json::from_cbor(vec1); try { @@ -2144,7 +2143,7 @@ TEST_CASE("CBOR roundtrips" * doctest::skip()) INFO_WITH_TEMP(filename + ": std::vector"); // parse JSON file std::ifstream f_json(filename); - json j1 = json::parse(f_json); + const json j1 = json::parse(f_json); // parse CBOR file const auto packed = utils::read_binary_file(filename + ".cbor"); @@ -2159,7 +2158,7 @@ TEST_CASE("CBOR roundtrips" * doctest::skip()) INFO_WITH_TEMP(filename + ": std::ifstream"); // parse JSON file std::ifstream f_json(filename); - json j1 = json::parse(f_json); + const json j1 = json::parse(f_json); // parse CBOR file std::ifstream f_cbor(filename + ".cbor", std::ios::binary); @@ -2174,7 +2173,7 @@ TEST_CASE("CBOR roundtrips" * doctest::skip()) INFO_WITH_TEMP(filename + ": uint8_t* and size"); // parse JSON file std::ifstream f_json(filename); - json j1 = json::parse(f_json); + const json j1 = json::parse(f_json); // parse CBOR file const auto packed = utils::read_binary_file(filename + ".cbor"); diff --git a/tests/src/unit-class_const_iterator.cpp b/tests/src/unit-class_const_iterator.cpp index 003ce88922..9e5de42c78 100644 --- a/tests/src/unit-class_const_iterator.cpp +++ b/tests/src/unit-class_const_iterator.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit-class_iterator.cpp b/tests/src/unit-class_iterator.cpp index 296ffa3ffc..2303381177 100644 --- a/tests/src/unit-class_iterator.cpp +++ b/tests/src/unit-class_iterator.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit-class_lexer.cpp b/tests/src/unit-class_lexer.cpp index ac02925313..64baf3da6d 100644 --- a/tests/src/unit-class_lexer.cpp +++ b/tests/src/unit-class_lexer.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -23,7 +23,7 @@ json::lexer::token_type scan_string(const char* s, const bool ignore_comments) } } // namespace -std::string get_error_message(const char* s, bool ignore_comments = false); +std::string get_error_message(const char* s, bool ignore_comments = false); // NOLINT(misc-use-internal-linkage) std::string get_error_message(const char* s, const bool ignore_comments) { auto ia = nlohmann::detail::input_adapter(s); diff --git a/tests/src/unit-class_parser.cpp b/tests/src/unit-class_parser.cpp index e2a8bac0f7..9c5fadc295 100644 --- a/tests/src/unit-class_parser.cpp +++ b/tests/src/unit-class_parser.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -75,7 +75,7 @@ class SaxEventLogger bool start_object(std::size_t elements) { - if (elements == static_cast(-1)) + if (elements == (std::numeric_limits::max)()) { events.emplace_back("start_object()"); } @@ -100,7 +100,7 @@ class SaxEventLogger bool start_array(std::size_t elements) { - if (elements == static_cast(-1)) + if (elements == (std::numeric_limits::max)()) { events.emplace_back("start_array()"); } @@ -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) { @@ -219,12 +220,14 @@ json parser_helper(const std::string& s) CHECK(j_nothrow == j); json j_sax; - nlohmann::detail::json_sax_dom_parser sdp(j_sax); + nlohmann::detail::json_sax_dom_parser sdp(j_sax); json::sax_parse(s, &sdp); CHECK(j_sax == j); 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 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") @@ -1366,7 +1402,7 @@ TEST_CASE("parser class") return event != json::parse_event_t::key; }; - json x = json::parse("{\"key\": false}", cb); + const json x = json::parse("{\"key\": false}", cb); CHECK(x == json::object()); } } @@ -1400,14 +1436,14 @@ TEST_CASE("parser class") SECTION("filter nothing") { - json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) noexcept + const json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) noexcept { return true; }); CHECK (j_object == json({{"foo", 2}, {"bar", {{"baz", 1}}}})); - json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) noexcept + const json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) noexcept { return true; }); @@ -1436,7 +1472,7 @@ TEST_CASE("parser class") SECTION("filter specific element") { - json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t event, const json & j) noexcept + const json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t event, const json & j) noexcept { // filter all number(2) elements return event != json::parse_event_t::value || j != json(2); @@ -1444,7 +1480,7 @@ TEST_CASE("parser class") CHECK (j_object == json({{"bar", {{"baz", 1}}}})); - json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t event, const json & j) noexcept + const json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t event, const json & j) noexcept { return event != json::parse_event_t::value || j != json(2); }); @@ -1454,7 +1490,7 @@ TEST_CASE("parser class") SECTION("filter object in array") { - json j_filtered1 = json::parse(structured_array, [](int /*unused*/, json::parse_event_t e, const json & parsed) + const json j_filtered1 = json::parse(structured_array, [](int /*unused*/, json::parse_event_t e, const json & parsed) { return !(e == json::parse_event_t::object_end && parsed.contains("foo")); }); @@ -1463,7 +1499,7 @@ TEST_CASE("parser class") CHECK (j_filtered1.size() == 2); CHECK (j_filtered1 == json({1, {{"qux", "baz"}}})); - json j_filtered2 = json::parse(structured_array, [](int /*unused*/, json::parse_event_t e, const json& /*parsed*/) noexcept + const json j_filtered2 = json::parse(structured_array, [](int /*unused*/, json::parse_event_t e, const json& /*parsed*/) noexcept { return e != json::parse_event_t::object_end; }); @@ -1478,7 +1514,7 @@ TEST_CASE("parser class") SECTION("first closing event") { { - json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t e, const json& /*unused*/) noexcept + const json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t e, const json& /*unused*/) noexcept { static bool first = true; if (e == json::parse_event_t::object_end && first) @@ -1495,7 +1531,7 @@ TEST_CASE("parser class") } { - json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t e, const json& /*unused*/) noexcept + const json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t e, const json& /*unused*/) noexcept { static bool first = true; if (e == json::parse_event_t::array_end && first) @@ -1519,13 +1555,13 @@ TEST_CASE("parser class") // object and array is discarded only after the closing character // has been read - json j_empty_object = json::parse("{}", [](int /*unused*/, json::parse_event_t e, const json& /*unused*/) noexcept + const json j_empty_object = json::parse("{}", [](int /*unused*/, json::parse_event_t e, const json& /*unused*/) noexcept { return e != json::parse_event_t::object_end; }); CHECK(j_empty_object == json()); - json j_empty_array = json::parse("[]", [](int /*unused*/, json::parse_event_t e, const json& /*unused*/) noexcept + const json j_empty_array = json::parse("[]", [](int /*unused*/, json::parse_event_t e, const json& /*unused*/) noexcept { return e != json::parse_event_t::array_end; }); @@ -1608,6 +1644,33 @@ TEST_CASE("parser class") SECTION("SAX parser") { + SECTION("null sax handler") + { +# if defined(__has_feature) +#if !__has_feature(undefined_behavior_sanitizer) + const std::string s = "some_string"; + SaxCountdown* p = nullptr; + CHECK_THROWS_WITH_AS(json::sax_parse(s, p), "[json.exception.other_error.502] SAX handler must not be null", json::other_error&); // NOLINT(clang-analyzer-core.NonNullParamChecker) + CHECK_THROWS_WITH_AS(json::sax_parse(s.begin(), s.end(), p), "[json.exception.other_error.502] SAX handler must not be null", json::other_error&); // NOLINT(clang-analyzer-core.NonNullParamChecker) + CHECK_THROWS_WITH_AS(json::sax_parse(nlohmann::detail::span_input_adapter(s.c_str(), s.size()), p), "[json.exception.other_error.502] SAX handler must not be null", json::other_error&); // NOLINT(clang-analyzer-core.NonNullParamChecker) +#endif +#else + const std::string s = "some_string"; + SaxCountdown* p = nullptr; + CHECK_THROWS_WITH_AS(json::sax_parse(s, p), "[json.exception.other_error.502] SAX handler must not be null", json::other_error&); // NOLINT(clang-analyzer-core.NonNullParamChecker) + CHECK_THROWS_WITH_AS(json::sax_parse(s.begin(), s.end(), p), "[json.exception.other_error.502] SAX handler must not be null", json::other_error&); // NOLINT(clang-analyzer-core.NonNullParamChecker) + CHECK_THROWS_WITH_AS(json::sax_parse(nlohmann::detail::span_input_adapter(s.c_str(), s.size()), p), "[json.exception.other_error.502] SAX handler must not be null", json::other_error&); // NOLINT(clang-analyzer-core.NonNullParamChecker) +#endif + } + + SECTION("valid sax handler") + { + const std::string str = "some_string"; + SaxCountdown s(1); + CHECK(json::sax_parse(str, &s) == false); + CHECK(json::sax_parse(nlohmann::detail::span_input_adapter(str.c_str(), str.size()), &s) == false); + } + SECTION("} without value") { SaxCountdown s(1); diff --git a/tests/src/unit-class_parser_diagnostic_positions.cpp b/tests/src/unit-class_parser_diagnostic_positions.cpp new file mode 100644 index 0000000000..2697ecf8af --- /dev/null +++ b/tests/src/unit-class_parser_diagnostic_positions.cpp @@ -0,0 +1,1957 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ (supporting code) +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + +#include "doctest_compatibility.h" +#define JSON_TESTS_PRIVATE +#ifdef JSON_DIAGNOSTIC_POSITIONS + #undef JSON_DIAGNOSTIC_POSITIONS +#endif + +#define JSON_DIAGNOSTIC_POSITIONS 1 +#include +using nlohmann::json; + +#ifdef JSON_TEST_NO_GLOBAL_UDLS + using namespace nlohmann::literals; // NOLINT(google-build-using-namespace) +#endif + +#include + +namespace +{ +class SaxEventLogger +{ + public: + bool null() + { + events.emplace_back("null()"); + return true; + } + + bool boolean(bool val) + { + events.emplace_back(val ? "boolean(true)" : "boolean(false)"); + return true; + } + + bool number_integer(json::number_integer_t val) + { + events.push_back("number_integer(" + std::to_string(val) + ")"); + return true; + } + + bool number_unsigned(json::number_unsigned_t val) + { + events.push_back("number_unsigned(" + std::to_string(val) + ")"); + return true; + } + + bool number_float(json::number_float_t /*unused*/, const std::string& s) + { + events.push_back("number_float(" + s + ")"); + return true; + } + + bool string(std::string& val) + { + events.push_back("string(" + val + ")"); + return true; + } + + bool binary(json::binary_t& val) + { + std::string binary_contents = "binary("; + std::string comma_space; + for (auto b : val) + { + binary_contents.append(comma_space); + binary_contents.append(std::to_string(static_cast(b))); + comma_space = ", "; + } + binary_contents.append(")"); + events.push_back(binary_contents); + return true; + } + + bool start_object(std::size_t elements) + { + if (elements == (std::numeric_limits::max)()) + { + events.emplace_back("start_object()"); + } + else + { + events.push_back("start_object(" + std::to_string(elements) + ")"); + } + return true; + } + + bool key(std::string& val) + { + events.push_back("key(" + val + ")"); + return true; + } + + bool end_object() + { + events.emplace_back("end_object()"); + return true; + } + + bool start_array(std::size_t elements) + { + if (elements == (std::numeric_limits::max)()) + { + events.emplace_back("start_array()"); + } + else + { + events.push_back("start_array(" + std::to_string(elements) + ")"); + } + return true; + } + + bool end_array() + { + events.emplace_back("end_array()"); + return true; + } + + bool parse_error(std::size_t position, const std::string& /*unused*/, const json::exception& /*unused*/) + { + errored = true; + events.push_back("parse_error(" + std::to_string(position) + ")"); + return false; + } + + std::vector events {}; // NOLINT(readability-redundant-member-init) + bool errored = false; +}; + +class SaxCountdown : public nlohmann::json::json_sax_t +{ + public: + explicit SaxCountdown(const int count) : events_left(count) + {} + + bool null() override + { + return events_left-- > 0; + } + + bool boolean(bool /*val*/) override + { + return events_left-- > 0; + } + + bool number_integer(json::number_integer_t /*val*/) override + { + return events_left-- > 0; + } + + bool number_unsigned(json::number_unsigned_t /*val*/) override + { + return events_left-- > 0; + } + + bool number_float(json::number_float_t /*val*/, const std::string& /*s*/) override + { + return events_left-- > 0; + } + + bool string(std::string& /*val*/) override + { + return events_left-- > 0; + } + + bool binary(json::binary_t& /*val*/) override + { + return events_left-- > 0; + } + + bool start_object(std::size_t /*elements*/) override + { + return events_left-- > 0; + } + + bool key(std::string& /*val*/) override + { + return events_left-- > 0; + } + + bool end_object() override + { + return events_left-- > 0; + } + + bool start_array(std::size_t /*elements*/) override + { + return events_left-- > 0; + } + + bool end_array() override + { + return events_left-- > 0; + } + + bool parse_error(std::size_t /*position*/, const std::string& /*last_token*/, const json::exception& /*ex*/) override + { + return false; + } + + private: + int events_left = 0; +}; + +json parser_helper(const std::string& s); +bool accept_helper(const std::string& s); +void comments_helper(const std::string& s); + +json parser_helper(const std::string& s) +{ + json j; + json::parser(nlohmann::detail::input_adapter(s)).parse(true, j); + + // if this line was reached, no exception occurred + // -> check if result is the same without exceptions + json j_nothrow; + CHECK_NOTHROW(json::parser(nlohmann::detail::input_adapter(s), nullptr, false).parse(true, j_nothrow)); + CHECK(j_nothrow == j); + + json j_sax; + nlohmann::detail::json_sax_dom_parser sdp(j_sax); + json::sax_parse(s, &sdp); + CHECK(j_sax == j); + + comments_helper(s); + + return j; +} + +bool accept_helper(const std::string& s) +{ + CAPTURE(s) + + // 1. parse s without exceptions + json j; + CHECK_NOTHROW(json::parser(nlohmann::detail::input_adapter(s), nullptr, false).parse(true, j)); + const bool ok_noexcept = !j.is_discarded(); + + // 2. accept s + const bool ok_accept = json::parser(nlohmann::detail::input_adapter(s)).accept(true); + + // 3. check if both approaches come to the same result + CHECK(ok_noexcept == ok_accept); + + // 4. parse with SAX (compare with relaxed accept result) + SaxEventLogger el; + CHECK_NOTHROW(json::sax_parse(s, &el, json::input_format_t::json, false)); + CHECK(json::parser(nlohmann::detail::input_adapter(s)).accept(false) == !el.errored); + + // 5. parse with simple callback + json::parser_callback_t const cb = [](int /*unused*/, json::parse_event_t /*unused*/, json& /*unused*/) noexcept + { + return true; + }; + json const j_cb = json::parse(s, cb, false); + const bool ok_noexcept_cb = !j_cb.is_discarded(); + + // 6. check if this approach came to the same result + CHECK(ok_noexcept == ok_noexcept_cb); + + // 7. check if comments are properly ignored + if (ok_accept) + { + comments_helper(s); + } + + // 8. return result + return ok_accept; +} + +void comments_helper(const std::string& s) +{ + json _; + + // parse/accept with default parser + CHECK_NOTHROW(_ = json::parse(s)); + CHECK(json::accept(s)); + + // parse/accept while skipping comments + CHECK_NOTHROW(_ = json::parse(s, nullptr, false, true)); + CHECK(json::accept(s, true)); + + std::vector json_with_comments; + + // start with a comment + json_with_comments.push_back(std::string("// this is a comment\n") + s); + json_with_comments.push_back(std::string("/* this is a comment */") + s); + // end with a comment + json_with_comments.push_back(s + "// this is a comment"); + json_with_comments.push_back(s + "/* this is a comment */"); + + // check all strings + for (const auto& json_with_comment : json_with_comments) + { + CAPTURE(json_with_comment) + CHECK_THROWS_AS(_ = json::parse(json_with_comment), json::parse_error); + CHECK(!json::accept(json_with_comment)); + + CHECK_NOTHROW(_ = json::parse(json_with_comment, nullptr, true, true)); + CHECK(json::accept(json_with_comment, true)); + } +} + +/** + * Validates that the generated JSON object is the same as expected + * Validates that the start position and end position match the start and end of the string + * + * This check assumes that there is no whitespace around the json object in the original string. + */ +void validate_generated_json_and_start_end_pos_helper(const std::string& original_string, const json& j, const json& check) +{ + CHECK(j == check); + CHECK(j.start_pos() == 0); + CHECK(j.end_pos() == original_string.size()); +} + +/** + * Parses the root object from the given root string and validates that the start and end positions for the nested object are correct. + * + * This checks that whitespace around the nested object is included in the start and end positions of the root object. + */ +void validate_start_end_pos_for_nested_obj_helper(const std::string& nested_type_json_str, const std::string& root_type_json_str, const json& expected_json, const json::parser_callback_t& cb = nullptr) +{ + json j; + + // 1. If callback is provided, use callback version of parse() + if (cb) + { + j = json::parse(root_type_json_str, cb); + } + else + { + j = json::parse(root_type_json_str); + } + + // 2. Check if the generated JSON is as expected + // Assumptions: The root_type_json_str does not have any whitespace around the json object + validate_generated_json_and_start_end_pos_helper(root_type_json_str, j, expected_json); + + // 3. Get the nested object + const auto& nested = j["nested"]; + // 4. Check if the start and end positions are generated correctly for nested objects and arrays + CHECK(nested_type_json_str == root_type_json_str.substr(nested.start_pos(), nested.end_pos() - nested.start_pos())); +} + +} // namespace + +TEST_CASE("parser class") +{ + SECTION("parse") + { + SECTION("null") + { + CHECK(parser_helper("null") == json(nullptr)); + } + + SECTION("true") + { + CHECK(parser_helper("true") == json(true)); + } + + SECTION("false") + { + CHECK(parser_helper("false") == json(false)); + } + + SECTION("array") + { + SECTION("empty array") + { + CHECK(parser_helper("[]") == json(json::value_t::array)); + CHECK(parser_helper("[ ]") == json(json::value_t::array)); + } + + SECTION("nonempty array") + { + CHECK(parser_helper("[true, false, null]") == json({true, false, nullptr})); + } + } + + SECTION("object") + { + SECTION("empty object") + { + CHECK(parser_helper("{}") == json(json::value_t::object)); + CHECK(parser_helper("{ }") == json(json::value_t::object)); + } + + SECTION("nonempty object") + { + CHECK(parser_helper("{\"\": true, \"one\": 1, \"two\": null}") == json({{"", true}, {"one", 1}, {"two", nullptr}})); + } + } + + SECTION("string") + { + // empty string + CHECK(parser_helper("\"\"") == json(json::value_t::string)); + + SECTION("errors") + { + // error: tab in string + CHECK_THROWS_WITH_AS(parser_helper("\"\t\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t; last read: '\"'", json::parse_error&); + // error: newline in string + CHECK_THROWS_WITH_AS(parser_helper("\"\n\""), "[json.exception.parse_error.101] parse error at line 2, column 0: syntax error while parsing value - invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\r\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r; last read: '\"'", json::parse_error&); + // error: backspace in string + CHECK_THROWS_WITH_AS(parser_helper("\"\b\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b; last read: '\"'", json::parse_error&); + // improve code coverage + CHECK_THROWS_AS(parser_helper("\uFF01"), json::parse_error&); + CHECK_THROWS_AS(parser_helper("[-4:1,]"), json::parse_error&); + // unescaped control characters + CHECK_THROWS_WITH_AS(parser_helper("\"\x00\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: missing closing quote; last read: '\"'", json::parse_error&); // NOLINT(bugprone-string-literal-with-embedded-nul) + CHECK_THROWS_WITH_AS(parser_helper("\"\x01\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0001 (SOH) must be escaped to \\u0001; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x02\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0002 (STX) must be escaped to \\u0002; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x03\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0003 (ETX) must be escaped to \\u0003; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x04\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0004 (EOT) must be escaped to \\u0004; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x05\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0005 (ENQ) must be escaped to \\u0005; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x06\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0006 (ACK) must be escaped to \\u0006; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x07\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0007 (BEL) must be escaped to \\u0007; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x08\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x09\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x0a\""), "[json.exception.parse_error.101] parse error at line 2, column 0: syntax error while parsing value - invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x0b\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000B (VT) must be escaped to \\u000B; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x0c\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x0d\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x0e\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000E (SO) must be escaped to \\u000E; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x0f\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000F (SI) must be escaped to \\u000F; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x10\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0010 (DLE) must be escaped to \\u0010; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x11\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0011 (DC1) must be escaped to \\u0011; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x12\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0012 (DC2) must be escaped to \\u0012; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x13\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0013 (DC3) must be escaped to \\u0013; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x14\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0014 (DC4) must be escaped to \\u0014; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x15\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0015 (NAK) must be escaped to \\u0015; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x16\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0016 (SYN) must be escaped to \\u0016; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x17\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0017 (ETB) must be escaped to \\u0017; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x18\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0018 (CAN) must be escaped to \\u0018; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x19\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0019 (EM) must be escaped to \\u0019; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x1a\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001A (SUB) must be escaped to \\u001A; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x1b\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001B (ESC) must be escaped to \\u001B; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x1c\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001C (FS) must be escaped to \\u001C; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x1d\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001D (GS) must be escaped to \\u001D; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x1e\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001E (RS) must be escaped to \\u001E; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x1f\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001F (US) must be escaped to \\u001F; last read: '\"'", json::parse_error&); + + SECTION("additional test for null byte") + { + // The test above for the null byte is wrong, because passing + // a string to the parser only reads int until it encounters + // a null byte. This test inserts the null byte later on and + // uses an iterator range. + std::string s = "\"1\""; + s[1] = '\0'; + json _; + CHECK_THROWS_WITH_AS(_ = json::parse(s.begin(), s.end()), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0000 (NUL) must be escaped to \\u0000; last read: '\"'", json::parse_error&); + } + } + + SECTION("escaped") + { + // quotation mark "\"" + auto r1 = R"("\"")"_json; + CHECK(parser_helper("\"\\\"\"") == r1); + // reverse solidus "\\" + auto r2 = R"("\\")"_json; + CHECK(parser_helper("\"\\\\\"") == r2); + // solidus + CHECK(parser_helper("\"\\/\"") == R"("/")"_json); + // backspace + CHECK(parser_helper("\"\\b\"") == json("\b")); + // formfeed + CHECK(parser_helper("\"\\f\"") == json("\f")); + // newline + CHECK(parser_helper("\"\\n\"") == json("\n")); + // carriage return + CHECK(parser_helper("\"\\r\"") == json("\r")); + // horizontal tab + CHECK(parser_helper("\"\\t\"") == json("\t")); + + CHECK(parser_helper("\"\\u0001\"").get() == "\x01"); + CHECK(parser_helper("\"\\u000a\"").get() == "\n"); + CHECK(parser_helper("\"\\u00b0\"").get() == "°"); + CHECK(parser_helper("\"\\u0c00\"").get() == "ఀ"); + CHECK(parser_helper("\"\\ud000\"").get() == "퀀"); + CHECK(parser_helper("\"\\u000E\"").get() == "\x0E"); + CHECK(parser_helper("\"\\u00F0\"").get() == "ð"); + CHECK(parser_helper("\"\\u0100\"").get() == "Ā"); + CHECK(parser_helper("\"\\u2000\"").get() == " "); + CHECK(parser_helper("\"\\uFFFF\"").get() == "￿"); + CHECK(parser_helper("\"\\u20AC\"").get() == "€"); + CHECK(parser_helper("\"€\"").get() == "€"); + CHECK(parser_helper("\"🎈\"").get() == "🎈"); + + CHECK(parser_helper("\"\\ud80c\\udc60\"").get() == "\xf0\x93\x81\xa0"); + CHECK(parser_helper("\"\\ud83c\\udf1e\"").get() == "🌞"); + } + } + + SECTION("number") + { + SECTION("integers") + { + SECTION("without exponent") + { + CHECK(parser_helper("-128") == json(-128)); + CHECK(parser_helper("-0") == json(-0)); + CHECK(parser_helper("0") == json(0)); + CHECK(parser_helper("128") == json(128)); + } + + SECTION("with exponent") + { + CHECK(parser_helper("0e1") == json(0e1)); + CHECK(parser_helper("0E1") == json(0e1)); + + CHECK(parser_helper("10000E-4") == json(10000e-4)); + CHECK(parser_helper("10000E-3") == json(10000e-3)); + CHECK(parser_helper("10000E-2") == json(10000e-2)); + CHECK(parser_helper("10000E-1") == json(10000e-1)); + CHECK(parser_helper("10000E0") == json(10000e0)); + CHECK(parser_helper("10000E1") == json(10000e1)); + CHECK(parser_helper("10000E2") == json(10000e2)); + CHECK(parser_helper("10000E3") == json(10000e3)); + CHECK(parser_helper("10000E4") == json(10000e4)); + + CHECK(parser_helper("10000e-4") == json(10000e-4)); + CHECK(parser_helper("10000e-3") == json(10000e-3)); + CHECK(parser_helper("10000e-2") == json(10000e-2)); + CHECK(parser_helper("10000e-1") == json(10000e-1)); + CHECK(parser_helper("10000e0") == json(10000e0)); + CHECK(parser_helper("10000e1") == json(10000e1)); + CHECK(parser_helper("10000e2") == json(10000e2)); + CHECK(parser_helper("10000e3") == json(10000e3)); + CHECK(parser_helper("10000e4") == json(10000e4)); + + CHECK(parser_helper("-0e1") == json(-0e1)); + CHECK(parser_helper("-0E1") == json(-0e1)); + CHECK(parser_helper("-0E123") == json(-0e123)); + + // numbers after exponent + CHECK(parser_helper("10E0") == json(10e0)); + CHECK(parser_helper("10E1") == json(10e1)); + CHECK(parser_helper("10E2") == json(10e2)); + CHECK(parser_helper("10E3") == json(10e3)); + CHECK(parser_helper("10E4") == json(10e4)); + CHECK(parser_helper("10E5") == json(10e5)); + CHECK(parser_helper("10E6") == json(10e6)); + CHECK(parser_helper("10E7") == json(10e7)); + CHECK(parser_helper("10E8") == json(10e8)); + CHECK(parser_helper("10E9") == json(10e9)); + CHECK(parser_helper("10E+0") == json(10e0)); + CHECK(parser_helper("10E+1") == json(10e1)); + CHECK(parser_helper("10E+2") == json(10e2)); + CHECK(parser_helper("10E+3") == json(10e3)); + CHECK(parser_helper("10E+4") == json(10e4)); + CHECK(parser_helper("10E+5") == json(10e5)); + CHECK(parser_helper("10E+6") == json(10e6)); + CHECK(parser_helper("10E+7") == json(10e7)); + CHECK(parser_helper("10E+8") == json(10e8)); + CHECK(parser_helper("10E+9") == json(10e9)); + CHECK(parser_helper("10E-1") == json(10e-1)); + CHECK(parser_helper("10E-2") == json(10e-2)); + CHECK(parser_helper("10E-3") == json(10e-3)); + CHECK(parser_helper("10E-4") == json(10e-4)); + CHECK(parser_helper("10E-5") == json(10e-5)); + CHECK(parser_helper("10E-6") == json(10e-6)); + CHECK(parser_helper("10E-7") == json(10e-7)); + CHECK(parser_helper("10E-8") == json(10e-8)); + CHECK(parser_helper("10E-9") == json(10e-9)); + } + + SECTION("edge cases") + { + // From RFC8259, Section 6: + // Note that when such software is used, numbers that are + // integers and are in the range [-(2**53)+1, (2**53)-1] + // are interoperable in the sense that implementations will + // agree exactly on their numeric values. + + // -(2**53)+1 + CHECK(parser_helper("-9007199254740991").get() == -9007199254740991); + // (2**53)-1 + CHECK(parser_helper("9007199254740991").get() == 9007199254740991); + } + + SECTION("over the edge cases") // issue #178 - Integer conversion to unsigned (incorrect handling of 64-bit integers) + { + // While RFC8259, Section 6 specifies a preference for support + // for ranges in range of IEEE 754-2008 binary64 (double precision) + // this does not accommodate 64-bit integers without loss of accuracy. + // As 64-bit integers are now widely used in software, it is desirable + // to expand support to the full 64 bit (signed and unsigned) range + // i.e. -(2**63) -> (2**64)-1. + + // -(2**63) ** Note: compilers see negative literals as negated positive numbers (hence the -1)) + CHECK(parser_helper("-9223372036854775808").get() == -9223372036854775807 - 1); + // (2**63)-1 + CHECK(parser_helper("9223372036854775807").get() == 9223372036854775807); + // (2**64)-1 + CHECK(parser_helper("18446744073709551615").get() == 18446744073709551615u); + } + } + + SECTION("floating-point") + { + SECTION("without exponent") + { + CHECK(parser_helper("-128.5") == json(-128.5)); + CHECK(parser_helper("0.999") == json(0.999)); + CHECK(parser_helper("128.5") == json(128.5)); + CHECK(parser_helper("-0.0") == json(-0.0)); + } + + SECTION("with exponent") + { + CHECK(parser_helper("-128.5E3") == json(-128.5E3)); + CHECK(parser_helper("-128.5E-3") == json(-128.5E-3)); + CHECK(parser_helper("-0.0e1") == json(-0.0e1)); + CHECK(parser_helper("-0.0E1") == json(-0.0e1)); + } + } + + SECTION("overflow") + { + // overflows during parsing yield an exception + CHECK_THROWS_WITH_AS(parser_helper("1.18973e+4932").empty(), "[json.exception.out_of_range.406] number overflow parsing '1.18973e+4932'", json::out_of_range&); + } + + SECTION("invalid numbers") + { + // numbers must not begin with "+" + CHECK_THROWS_AS(parser_helper("+1"), json::parse_error&); + CHECK_THROWS_AS(parser_helper("+0"), json::parse_error&); + + CHECK_THROWS_WITH_AS(parser_helper("01"), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - unexpected number literal; expected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-01"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - unexpected number literal; expected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("--1"), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid number; expected digit after '-'; last read: '--'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("1."), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected digit after '.'; last read: '1.'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("1E"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1E'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("1E-"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid number; expected digit after exponent sign; last read: '1E-'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("1.E1"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected digit after '.'; last read: '1.E'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-1E"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '-1E'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-0E#"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '-0E#'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-0E-#"), + "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid number; expected digit after exponent sign; last read: '-0E-#'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-0#"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid literal; last read: '-0#'; expected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-0.0:"), + "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - unexpected ':'; expected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-0.0Z"), + "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid literal; last read: '-0.0Z'; expected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-0E123:"), + "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing value - unexpected ':'; expected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-0e0-:"), + "[json.exception.parse_error.101] parse error at line 1, column 6: syntax error while parsing value - invalid number; expected digit after '-'; last read: '-:'; expected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-0e-:"), + "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid number; expected digit after exponent sign; last read: '-0e-:'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-0f"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: '-0f'; expected end of input", json::parse_error&); + } + } + } + + SECTION("accept") + { + SECTION("null") + { + CHECK(accept_helper("null")); + } + + SECTION("true") + { + CHECK(accept_helper("true")); + } + + SECTION("false") + { + CHECK(accept_helper("false")); + } + + SECTION("array") + { + SECTION("empty array") + { + CHECK(accept_helper("[]")); + CHECK(accept_helper("[ ]")); + } + + SECTION("nonempty array") + { + CHECK(accept_helper("[true, false, null]")); + } + } + + SECTION("object") + { + SECTION("empty object") + { + CHECK(accept_helper("{}")); + CHECK(accept_helper("{ }")); + } + + SECTION("nonempty object") + { + CHECK(accept_helper("{\"\": true, \"one\": 1, \"two\": null}")); + } + } + + SECTION("string") + { + // empty string + CHECK(accept_helper("\"\"")); + + SECTION("errors") + { + // error: tab in string + CHECK(accept_helper("\"\t\"") == false); + // error: newline in string + CHECK(accept_helper("\"\n\"") == false); + CHECK(accept_helper("\"\r\"") == false); + // error: backspace in string + CHECK(accept_helper("\"\b\"") == false); + // improve code coverage + CHECK(accept_helper("\uFF01") == false); + CHECK(accept_helper("[-4:1,]") == false); + // unescaped control characters + CHECK(accept_helper("\"\x00\"") == false); // NOLINT(bugprone-string-literal-with-embedded-nul) + CHECK(accept_helper("\"\x01\"") == false); + CHECK(accept_helper("\"\x02\"") == false); + CHECK(accept_helper("\"\x03\"") == false); + CHECK(accept_helper("\"\x04\"") == false); + CHECK(accept_helper("\"\x05\"") == false); + CHECK(accept_helper("\"\x06\"") == false); + CHECK(accept_helper("\"\x07\"") == false); + CHECK(accept_helper("\"\x08\"") == false); + CHECK(accept_helper("\"\x09\"") == false); + CHECK(accept_helper("\"\x0a\"") == false); + CHECK(accept_helper("\"\x0b\"") == false); + CHECK(accept_helper("\"\x0c\"") == false); + CHECK(accept_helper("\"\x0d\"") == false); + CHECK(accept_helper("\"\x0e\"") == false); + CHECK(accept_helper("\"\x0f\"") == false); + CHECK(accept_helper("\"\x10\"") == false); + CHECK(accept_helper("\"\x11\"") == false); + CHECK(accept_helper("\"\x12\"") == false); + CHECK(accept_helper("\"\x13\"") == false); + CHECK(accept_helper("\"\x14\"") == false); + CHECK(accept_helper("\"\x15\"") == false); + CHECK(accept_helper("\"\x16\"") == false); + CHECK(accept_helper("\"\x17\"") == false); + CHECK(accept_helper("\"\x18\"") == false); + CHECK(accept_helper("\"\x19\"") == false); + CHECK(accept_helper("\"\x1a\"") == false); + CHECK(accept_helper("\"\x1b\"") == false); + CHECK(accept_helper("\"\x1c\"") == false); + CHECK(accept_helper("\"\x1d\"") == false); + CHECK(accept_helper("\"\x1e\"") == false); + CHECK(accept_helper("\"\x1f\"") == false); + } + + SECTION("escaped") + { + // quotation mark "\"" + auto r1 = R"("\"")"_json; + CHECK(accept_helper("\"\\\"\"")); + // reverse solidus "\\" + auto r2 = R"("\\")"_json; + CHECK(accept_helper("\"\\\\\"")); + // solidus + CHECK(accept_helper("\"\\/\"")); + // backspace + CHECK(accept_helper("\"\\b\"")); + // formfeed + CHECK(accept_helper("\"\\f\"")); + // newline + CHECK(accept_helper("\"\\n\"")); + // carriage return + CHECK(accept_helper("\"\\r\"")); + // horizontal tab + CHECK(accept_helper("\"\\t\"")); + + CHECK(accept_helper("\"\\u0001\"")); + CHECK(accept_helper("\"\\u000a\"")); + CHECK(accept_helper("\"\\u00b0\"")); + CHECK(accept_helper("\"\\u0c00\"")); + CHECK(accept_helper("\"\\ud000\"")); + CHECK(accept_helper("\"\\u000E\"")); + CHECK(accept_helper("\"\\u00F0\"")); + CHECK(accept_helper("\"\\u0100\"")); + CHECK(accept_helper("\"\\u2000\"")); + CHECK(accept_helper("\"\\uFFFF\"")); + CHECK(accept_helper("\"\\u20AC\"")); + CHECK(accept_helper("\"€\"")); + CHECK(accept_helper("\"🎈\"")); + + CHECK(accept_helper("\"\\ud80c\\udc60\"")); + CHECK(accept_helper("\"\\ud83c\\udf1e\"")); + } + } + + SECTION("number") + { + SECTION("integers") + { + SECTION("without exponent") + { + CHECK(accept_helper("-128")); + CHECK(accept_helper("-0")); + CHECK(accept_helper("0")); + CHECK(accept_helper("128")); + } + + SECTION("with exponent") + { + CHECK(accept_helper("0e1")); + CHECK(accept_helper("0E1")); + + CHECK(accept_helper("10000E-4")); + CHECK(accept_helper("10000E-3")); + CHECK(accept_helper("10000E-2")); + CHECK(accept_helper("10000E-1")); + CHECK(accept_helper("10000E0")); + CHECK(accept_helper("10000E1")); + CHECK(accept_helper("10000E2")); + CHECK(accept_helper("10000E3")); + CHECK(accept_helper("10000E4")); + + CHECK(accept_helper("10000e-4")); + CHECK(accept_helper("10000e-3")); + CHECK(accept_helper("10000e-2")); + CHECK(accept_helper("10000e-1")); + CHECK(accept_helper("10000e0")); + CHECK(accept_helper("10000e1")); + CHECK(accept_helper("10000e2")); + CHECK(accept_helper("10000e3")); + CHECK(accept_helper("10000e4")); + + CHECK(accept_helper("-0e1")); + CHECK(accept_helper("-0E1")); + CHECK(accept_helper("-0E123")); + } + + SECTION("edge cases") + { + // From RFC8259, Section 6: + // Note that when such software is used, numbers that are + // integers and are in the range [-(2**53)+1, (2**53)-1] + // are interoperable in the sense that implementations will + // agree exactly on their numeric values. + + // -(2**53)+1 + CHECK(accept_helper("-9007199254740991")); + // (2**53)-1 + CHECK(accept_helper("9007199254740991")); + } + + SECTION("over the edge cases") // issue #178 - Integer conversion to unsigned (incorrect handling of 64-bit integers) + { + // While RFC8259, Section 6 specifies a preference for support + // for ranges in range of IEEE 754-2008 binary64 (double precision) + // this does not accommodate 64 bit integers without loss of accuracy. + // As 64 bit integers are now widely used in software, it is desirable + // to expand support to the full 64 bit (signed and unsigned) range + // i.e. -(2**63) -> (2**64)-1. + + // -(2**63) ** Note: compilers see negative literals as negated positive numbers (hence the -1)) + CHECK(accept_helper("-9223372036854775808")); + // (2**63)-1 + CHECK(accept_helper("9223372036854775807")); + // (2**64)-1 + CHECK(accept_helper("18446744073709551615")); + } + } + + SECTION("floating-point") + { + SECTION("without exponent") + { + CHECK(accept_helper("-128.5")); + CHECK(accept_helper("0.999")); + CHECK(accept_helper("128.5")); + CHECK(accept_helper("-0.0")); + } + + SECTION("with exponent") + { + CHECK(accept_helper("-128.5E3")); + CHECK(accept_helper("-128.5E-3")); + CHECK(accept_helper("-0.0e1")); + CHECK(accept_helper("-0.0E1")); + } + } + + SECTION("overflow") + { + // overflows during parsing + CHECK(!accept_helper("1.18973e+4932")); + } + + SECTION("invalid numbers") + { + CHECK(accept_helper("01") == false); + CHECK(accept_helper("--1") == false); + CHECK(accept_helper("1.") == false); + CHECK(accept_helper("1E") == false); + CHECK(accept_helper("1E-") == false); + CHECK(accept_helper("1.E1") == false); + CHECK(accept_helper("-1E") == false); + CHECK(accept_helper("-0E#") == false); + CHECK(accept_helper("-0E-#") == false); + CHECK(accept_helper("-0#") == false); + CHECK(accept_helper("-0.0:") == false); + CHECK(accept_helper("-0.0Z") == false); + CHECK(accept_helper("-0E123:") == false); + CHECK(accept_helper("-0e0-:") == false); + CHECK(accept_helper("-0e-:") == false); + CHECK(accept_helper("-0f") == false); + + // numbers must not begin with "+" + CHECK(accept_helper("+1") == false); + CHECK(accept_helper("+0") == false); + } + } + } + + SECTION("parse errors") + { + // unexpected end of number + CHECK_THROWS_WITH_AS(parser_helper("0."), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected digit after '.'; last read: '0.'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-"), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid number; expected digit after '-'; last read: '-'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("--"), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid number; expected digit after '-'; last read: '--'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-0."), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid number; expected digit after '.'; last read: '-0.'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-."), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid number; expected digit after '-'; last read: '-.'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-:"), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid number; expected digit after '-'; last read: '-:'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("0.:"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected digit after '.'; last read: '0.:'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("e."), + "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - invalid literal; last read: 'e'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("1e."), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1e.'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("1e/"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1e/'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("1e:"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1e:'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("1E."), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1E.'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("1E/"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1E/'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("1E:"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1E:'", json::parse_error&); + + // unexpected end of null + CHECK_THROWS_WITH_AS(parser_helper("n"), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid literal; last read: 'n'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("nu"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid literal; last read: 'nu'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("nul"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'nul'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("nulk"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'nulk'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("nulm"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'nulm'", json::parse_error&); + + // unexpected end of true + CHECK_THROWS_WITH_AS(parser_helper("t"), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid literal; last read: 't'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("tr"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid literal; last read: 'tr'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("tru"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'tru'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("trud"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'trud'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("truf"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'truf'", json::parse_error&); + + // unexpected end of false + CHECK_THROWS_WITH_AS(parser_helper("f"), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid literal; last read: 'f'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("fa"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid literal; last read: 'fa'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("fal"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'fal'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("fals"), + "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid literal; last read: 'fals'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("falsd"), + "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid literal; last read: 'falsd'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("falsf"), + "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid literal; last read: 'falsf'", json::parse_error&); + + // missing/unexpected end of array + CHECK_THROWS_WITH_AS(parser_helper("["), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("[1"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing array - unexpected end of input; expected ']'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("[1,"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("[1,]"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("]"), + "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal", json::parse_error&); + + // missing/unexpected end of object + CHECK_THROWS_WITH_AS(parser_helper("{"), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing object key - unexpected end of input; expected string literal", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("{\"foo\""), + "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing object separator - unexpected end of input; expected ':'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("{\"foo\":"), + "[json.exception.parse_error.101] parse error at line 1, column 8: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("{\"foo\":}"), + "[json.exception.parse_error.101] parse error at line 1, column 8: syntax error while parsing value - unexpected '}'; expected '[', '{', or a literal", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("{\"foo\":1,}"), + "[json.exception.parse_error.101] parse error at line 1, column 10: syntax error while parsing object key - unexpected '}'; expected string literal", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("}"), + "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected '}'; expected '[', '{', or a literal", json::parse_error&); + + // missing/unexpected end of string + CHECK_THROWS_WITH_AS(parser_helper("\""), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: missing closing quote; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\\\""), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid string: missing closing quote; last read: '\"\\\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\\u\""), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\\u0\""), + "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u0\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\\u01\""), + "[json.exception.parse_error.101] parse error at line 1, column 6: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u01\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\\u012\""), + "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u012\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\\u"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\\u0"), + "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u0'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\\u01"), + "[json.exception.parse_error.101] parse error at line 1, column 6: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u01'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\\u012"), + "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u012'", json::parse_error&); + + // invalid escapes + for (int c = 1; c < 128; ++c) + { + auto s = std::string("\"\\") + std::string(1, static_cast(c)) + "\""; + + switch (c) + { + // valid escapes + case ('"'): + case ('\\'): + case ('/'): + case ('b'): + case ('f'): + case ('n'): + case ('r'): + case ('t'): + { + CHECK_NOTHROW(parser_helper(s)); + break; + } + + // \u must be followed with four numbers, so we skip it here + case ('u'): + { + break; + } + + // any other combination of backslash and character is invalid + default: + { + CHECK_THROWS_AS(parser_helper(s), json::parse_error&); + // only check error message if c is not a control character + if (c > 0x1f) + { + CHECK_THROWS_WITH_STD_STR(parser_helper(s), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid string: forbidden character after backslash; last read: '\"\\" + std::string(1, static_cast(c)) + "'"); + } + break; + } + } + } + + // invalid \uxxxx escapes + { + // check whether character is a valid hex character + const auto valid = [](int c) + { + switch (c) + { + case ('0'): + case ('1'): + case ('2'): + case ('3'): + case ('4'): + case ('5'): + case ('6'): + case ('7'): + case ('8'): + case ('9'): + case ('a'): + case ('b'): + case ('c'): + case ('d'): + case ('e'): + case ('f'): + case ('A'): + case ('B'): + case ('C'): + case ('D'): + case ('E'): + case ('F'): + { + return true; + } + + default: + { + return false; + } + } + }; + + for (int c = 1; c < 128; ++c) + { + std::string const s = "\"\\u"; + + // create a string with the iterated character at each position + auto s1 = s + "000" + std::string(1, static_cast(c)) + "\""; + auto s2 = s + "00" + std::string(1, static_cast(c)) + "0\""; + auto s3 = s + "0" + std::string(1, static_cast(c)) + "00\""; + auto s4 = s + std::string(1, static_cast(c)) + "000\""; + + if (valid(c)) + { + CAPTURE(s1) + CHECK_NOTHROW(parser_helper(s1)); + CAPTURE(s2) + CHECK_NOTHROW(parser_helper(s2)); + CAPTURE(s3) + CHECK_NOTHROW(parser_helper(s3)); + CAPTURE(s4) + CHECK_NOTHROW(parser_helper(s4)); + } + else + { + CAPTURE(s1) + CHECK_THROWS_AS(parser_helper(s1), json::parse_error&); + // only check error message if c is not a control character + if (c > 0x1f) + { + CHECK_THROWS_WITH_STD_STR(parser_helper(s1), + "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '" + s1.substr(0, 7) + "'"); + } + + CAPTURE(s2) + CHECK_THROWS_AS(parser_helper(s2), json::parse_error&); + // only check error message if c is not a control character + if (c > 0x1f) + { + CHECK_THROWS_WITH_STD_STR(parser_helper(s2), + "[json.exception.parse_error.101] parse error at line 1, column 6: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '" + s2.substr(0, 6) + "'"); + } + + CAPTURE(s3) + CHECK_THROWS_AS(parser_helper(s3), json::parse_error&); + // only check error message if c is not a control character + if (c > 0x1f) + { + CHECK_THROWS_WITH_STD_STR(parser_helper(s3), + "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '" + s3.substr(0, 5) + "'"); + } + + CAPTURE(s4) + CHECK_THROWS_AS(parser_helper(s4), json::parse_error&); + // only check error message if c is not a control character + if (c > 0x1f) + { + CHECK_THROWS_WITH_STD_STR(parser_helper(s4), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '" + s4.substr(0, 4) + "'"); + } + } + } + } + + json _; + + // missing part of a surrogate pair + CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD80C\""), "[json.exception.parse_error.101] parse error at line 1, column 8: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD80C\"'", json::parse_error&); + // invalid surrogate pair + CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD80C\\uD80C\""), + "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD80C\\uD80C'", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD80C\\u0000\""), + "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD80C\\u0000'", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD80C\\uFFFF\""), + "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD80C\\uFFFF'", json::parse_error&); + } + + SECTION("parse errors (accept)") + { + // unexpected end of number + CHECK(accept_helper("0.") == false); + CHECK(accept_helper("-") == false); + CHECK(accept_helper("--") == false); + CHECK(accept_helper("-0.") == false); + CHECK(accept_helper("-.") == false); + CHECK(accept_helper("-:") == false); + CHECK(accept_helper("0.:") == false); + CHECK(accept_helper("e.") == false); + CHECK(accept_helper("1e.") == false); + CHECK(accept_helper("1e/") == false); + CHECK(accept_helper("1e:") == false); + CHECK(accept_helper("1E.") == false); + CHECK(accept_helper("1E/") == false); + CHECK(accept_helper("1E:") == false); + + // unexpected end of null + CHECK(accept_helper("n") == false); + CHECK(accept_helper("nu") == false); + CHECK(accept_helper("nul") == false); + + // unexpected end of true + CHECK(accept_helper("t") == false); + CHECK(accept_helper("tr") == false); + CHECK(accept_helper("tru") == false); + + // unexpected end of false + CHECK(accept_helper("f") == false); + CHECK(accept_helper("fa") == false); + CHECK(accept_helper("fal") == false); + CHECK(accept_helper("fals") == false); + + // missing/unexpected end of array + CHECK(accept_helper("[") == false); + CHECK(accept_helper("[1") == false); + CHECK(accept_helper("[1,") == false); + CHECK(accept_helper("[1,]") == false); + CHECK(accept_helper("]") == false); + + // missing/unexpected end of object + CHECK(accept_helper("{") == false); + CHECK(accept_helper("{\"foo\"") == false); + CHECK(accept_helper("{\"foo\":") == false); + CHECK(accept_helper("{\"foo\":}") == false); + CHECK(accept_helper("{\"foo\":1,}") == false); + CHECK(accept_helper("}") == false); + + // missing/unexpected end of string + CHECK(accept_helper("\"") == false); + CHECK(accept_helper("\"\\\"") == false); + CHECK(accept_helper("\"\\u\"") == false); + CHECK(accept_helper("\"\\u0\"") == false); + CHECK(accept_helper("\"\\u01\"") == false); + CHECK(accept_helper("\"\\u012\"") == false); + CHECK(accept_helper("\"\\u") == false); + CHECK(accept_helper("\"\\u0") == false); + CHECK(accept_helper("\"\\u01") == false); + CHECK(accept_helper("\"\\u012") == false); + + // unget of newline + CHECK(parser_helper("\n123\n") == 123); + + // invalid escapes + for (int c = 1; c < 128; ++c) + { + auto s = std::string("\"\\") + std::string(1, static_cast(c)) + "\""; + + switch (c) + { + // valid escapes + case ('"'): + case ('\\'): + case ('/'): + case ('b'): + case ('f'): + case ('n'): + case ('r'): + case ('t'): + { + CHECK(json::parser(nlohmann::detail::input_adapter(s)).accept()); + break; + } + + // \u must be followed with four numbers, so we skip it here + case ('u'): + { + break; + } + + // any other combination of backslash and character is invalid + default: + { + CHECK(json::parser(nlohmann::detail::input_adapter(s)).accept() == false); + break; + } + } + } + + // invalid \uxxxx escapes + { + // check whether character is a valid hex character + const auto valid = [](int c) + { + switch (c) + { + case ('0'): + case ('1'): + case ('2'): + case ('3'): + case ('4'): + case ('5'): + case ('6'): + case ('7'): + case ('8'): + case ('9'): + case ('a'): + case ('b'): + case ('c'): + case ('d'): + case ('e'): + case ('f'): + case ('A'): + case ('B'): + case ('C'): + case ('D'): + case ('E'): + case ('F'): + { + return true; + } + + default: + { + return false; + } + } + }; + + for (int c = 1; c < 128; ++c) + { + std::string const s = "\"\\u"; + + // create a string with the iterated character at each position + const auto s1 = s + "000" + std::string(1, static_cast(c)) + "\""; + const auto s2 = s + "00" + std::string(1, static_cast(c)) + "0\""; + const auto s3 = s + "0" + std::string(1, static_cast(c)) + "00\""; + const auto s4 = s + std::string(1, static_cast(c)) + "000\""; + + if (valid(c)) + { + CAPTURE(s1) + CHECK(json::parser(nlohmann::detail::input_adapter(s1)).accept()); + CAPTURE(s2) + CHECK(json::parser(nlohmann::detail::input_adapter(s2)).accept()); + CAPTURE(s3) + CHECK(json::parser(nlohmann::detail::input_adapter(s3)).accept()); + CAPTURE(s4) + CHECK(json::parser(nlohmann::detail::input_adapter(s4)).accept()); + } + else + { + CAPTURE(s1) + CHECK(json::parser(nlohmann::detail::input_adapter(s1)).accept() == false); + + CAPTURE(s2) + CHECK(json::parser(nlohmann::detail::input_adapter(s2)).accept() == false); + + CAPTURE(s3) + CHECK(json::parser(nlohmann::detail::input_adapter(s3)).accept() == false); + + CAPTURE(s4) + CHECK(json::parser(nlohmann::detail::input_adapter(s4)).accept() == false); + } + } + } + + // missing part of a surrogate pair + CHECK(accept_helper("\"\\uD80C\"") == false); + // invalid surrogate pair + CHECK(accept_helper("\"\\uD80C\\uD80C\"") == false); + CHECK(accept_helper("\"\\uD80C\\u0000\"") == false); + CHECK(accept_helper("\"\\uD80C\\uFFFF\"") == false); + } + + SECTION("tests found by mutate++") + { + // test case to make sure no comma precedes the first key + CHECK_THROWS_WITH_AS(parser_helper("{,\"key\": false}"), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing object key - unexpected ','; expected string literal", json::parse_error&); + // test case to make sure an object is properly closed + CHECK_THROWS_WITH_AS(parser_helper("[{\"key\": false true]"), "[json.exception.parse_error.101] parse error at line 1, column 19: syntax error while parsing object - unexpected true literal; expected '}'", json::parse_error&); + + // test case to make sure the callback is properly evaluated after reading a key + { + json::parser_callback_t const cb = [](int /*unused*/, json::parse_event_t event, json& /*unused*/) noexcept + { + return event != json::parse_event_t::key; + }; + + const json x = json::parse("{\"key\": false}", cb); + CHECK(x == json::object()); + } + } + + SECTION("callback function") + { + const auto* s_object = R"( + { + "foo": 2, + "bar": { + "baz": 1 + } + } + )"; + + const auto* s_array = R"( + [1,2,[3,4,5],4,5] + )"; + + const auto* structured_array = R"( + [ + 1, + { + "foo": "bar" + }, + { + "qux": "baz" + } + ] + )"; + + SECTION("filter nothing") + { + const json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) noexcept + { + return true; + }); + + CHECK (j_object == json({{"foo", 2}, {"bar", {{"baz", 1}}}})); + + const json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) noexcept + { + return true; + }); + + CHECK (j_array == json({1, 2, {3, 4, 5}, 4, 5})); + } + + SECTION("filter everything") + { + json const j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) noexcept + { + return false; + }); + + // the top-level object will be discarded, leaving a null + CHECK (j_object.is_null()); + + json const j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) noexcept + { + return false; + }); + + // the top-level array will be discarded, leaving a null + CHECK (j_array.is_null()); + } + + SECTION("filter specific element") + { + const json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t event, const json & j) noexcept + { + // filter all number(2) elements + return event != json::parse_event_t::value || j != json(2); + }); + + CHECK (j_object == json({{"bar", {{"baz", 1}}}})); + + const json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t event, const json & j) noexcept + { + return event != json::parse_event_t::value || j != json(2); + }); + + CHECK (j_array == json({1, {3, 4, 5}, 4, 5})); + } + + SECTION("filter object in array") + { + const json j_filtered1 = json::parse(structured_array, [](int /*unused*/, json::parse_event_t e, const json & parsed) + { + return !(e == json::parse_event_t::object_end && parsed.contains("foo")); + }); + + // the specified object will be discarded, and removed. + CHECK (j_filtered1.size() == 2); + CHECK (j_filtered1 == json({1, {{"qux", "baz"}}})); + + const json j_filtered2 = json::parse(structured_array, [](int /*unused*/, json::parse_event_t e, const json& /*parsed*/) noexcept + { + return e != json::parse_event_t::object_end; + }); + + // removed all objects in array. + CHECK (j_filtered2.size() == 1); + CHECK (j_filtered2 == json({1})); + } + + SECTION("filter specific events") + { + SECTION("first closing event") + { + { + const json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t e, const json& /*unused*/) noexcept + { + static bool first = true; + if (e == json::parse_event_t::object_end && first) + { + first = false; + return false; + } + + return true; + }); + + // the first completed object will be discarded + CHECK (j_object == json({{"foo", 2}})); + } + + { + const json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t e, const json& /*unused*/) noexcept + { + static bool first = true; + if (e == json::parse_event_t::array_end && first) + { + first = false; + return false; + } + + return true; + }); + + // the first completed array will be discarded + CHECK (j_array == json({1, 2, 4, 5})); + } + } + } + + SECTION("special cases") + { + // the following test cases cover the situation in which an empty + // object and array is discarded only after the closing character + // has been read + + const json j_empty_object = json::parse("{}", [](int /*unused*/, json::parse_event_t e, const json& /*unused*/) noexcept + { + return e != json::parse_event_t::object_end; + }); + CHECK(j_empty_object == json()); + + const json j_empty_array = json::parse("[]", [](int /*unused*/, json::parse_event_t e, const json& /*unused*/) noexcept + { + return e != json::parse_event_t::array_end; + }); + CHECK(j_empty_array == json()); + } + } + + SECTION("constructing from contiguous containers") + { + SECTION("from std::vector") + { + std::vector v = {'t', 'r', 'u', 'e'}; + json j; + json::parser(nlohmann::detail::input_adapter(std::begin(v), std::end(v))).parse(true, j); + CHECK(j == json(true)); + } + + SECTION("from std::array") + { + std::array v { {'t', 'r', 'u', 'e'} }; + json j; + json::parser(nlohmann::detail::input_adapter(std::begin(v), std::end(v))).parse(true, j); + CHECK(j == json(true)); + } + + SECTION("from array") + { + uint8_t v[] = {'t', 'r', 'u', 'e'}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + json j; + json::parser(nlohmann::detail::input_adapter(std::begin(v), std::end(v))).parse(true, j); + CHECK(j == json(true)); + } + + SECTION("from char literal") + { + CHECK(parser_helper("true") == json(true)); + } + + SECTION("from std::string") + { + std::string v = {'t', 'r', 'u', 'e'}; + json j; + json::parser(nlohmann::detail::input_adapter(std::begin(v), std::end(v))).parse(true, j); + CHECK(j == json(true)); + } + + SECTION("from std::initializer_list") + { + std::initializer_list const v = {'t', 'r', 'u', 'e'}; + json j; + json::parser(nlohmann::detail::input_adapter(std::begin(v), std::end(v))).parse(true, j); + CHECK(j == json(true)); + } + + SECTION("from std::valarray") + { + std::valarray v = {'t', 'r', 'u', 'e'}; + json j; + json::parser(nlohmann::detail::input_adapter(std::begin(v), std::end(v))).parse(true, j); + CHECK(j == json(true)); + } + } + + SECTION("improve test coverage") + { + SECTION("parser with callback") + { + json::parser_callback_t const cb = [](int /*unused*/, json::parse_event_t /*unused*/, json& /*unused*/) noexcept + { + return true; + }; + + CHECK(json::parse("{\"foo\": true:", cb, false).is_discarded()); + + json _; + CHECK_THROWS_WITH_AS(_ = json::parse("{\"foo\": true:", cb), "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing object - unexpected ':'; expected '}'", json::parse_error&); + + CHECK_THROWS_WITH_AS(_ = json::parse("1.18973e+4932", cb), "[json.exception.out_of_range.406] number overflow parsing '1.18973e+4932'", json::out_of_range&); + } + + SECTION("SAX parser") + { + SECTION("} without value") + { + SaxCountdown s(1); + CHECK(json::sax_parse("{}", &s) == false); + } + + SECTION("} with value") + { + SaxCountdown s(3); + CHECK(json::sax_parse("{\"k1\": true}", &s) == false); + } + + SECTION("second key") + { + SaxCountdown s(3); + CHECK(json::sax_parse("{\"k1\": true, \"k2\": false}", &s) == false); + } + + SECTION("] without value") + { + SaxCountdown s(1); + CHECK(json::sax_parse("[]", &s) == false); + } + + SECTION("] with value") + { + SaxCountdown s(2); + CHECK(json::sax_parse("[1]", &s) == false); + } + + SECTION("float") + { + SaxCountdown s(0); + CHECK(json::sax_parse("3.14", &s) == false); + } + + SECTION("false") + { + SaxCountdown s(0); + CHECK(json::sax_parse("false", &s) == false); + } + + SECTION("null") + { + SaxCountdown s(0); + CHECK(json::sax_parse("null", &s) == false); + } + + SECTION("true") + { + SaxCountdown s(0); + CHECK(json::sax_parse("true", &s) == false); + } + + SECTION("unsigned") + { + SaxCountdown s(0); + CHECK(json::sax_parse("12", &s) == false); + } + + SECTION("integer") + { + SaxCountdown s(0); + CHECK(json::sax_parse("-12", &s) == false); + } + + SECTION("string") + { + SaxCountdown s(0); + CHECK(json::sax_parse("\"foo\"", &s) == false); + } + } + } + + SECTION("error messages for comments") + { + json _; + CHECK_THROWS_WITH_AS(_ = json::parse("/a", nullptr, true, true), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid comment; expecting '/' or '*' after '/'; last read: '/a'", json::parse_error); + CHECK_THROWS_WITH_AS(_ = json::parse("/*", nullptr, true, true), "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid comment; missing closing '*/'; last read: '/*'", json::parse_error); + } + + // Macro for all test cases for start_pos and end_pos +#define SETUP_TESTCASES() \ + SECTION("with callback") \ + { \ + SECTION("filter nothing") \ + { \ + json::parser_callback_t const cb = [](int /*unused*/, json::parse_event_t /*unused*/, json& /*unused*/) noexcept \ + { \ + return true; \ + }; \ + validate_start_end_pos_for_nested_obj_helper(nested_type_json_str, root_type_json_str, expected, cb); \ + } \ + SECTION("filter element") \ + { \ + json::parser_callback_t const cb = [](int /*unused*/, json::parse_event_t event, json& j) noexcept \ + { \ + return (event != json::parse_event_t::key && event != json::parse_event_t::value) || j != json("a"); \ + }; \ + validate_start_end_pos_for_nested_obj_helper(nested_type_json_str, root_type_json_str, filteredExpected, cb); \ + } \ + } \ + SECTION("without callback") \ + { \ + validate_start_end_pos_for_nested_obj_helper(nested_type_json_str, root_type_json_str, expected); \ + } + + SECTION("retrieve start position and end position") + { + SECTION("for object") + { + // Create an object with spaces to test the start and end positions. Spaces will not be included in the + // JSON object, however, the start and end positions should include the spaces from the input JSON string. + const std::string nested_type_json_str = R"({ "a": 1,"b" : "test1"})"; + const std::string root_type_json_str = R"({ "nested": )" + nested_type_json_str + R"(, "anotherValue": "test2"})"; + auto expected = json({{"nested", {{"a", 1}, {"b", "test1"}}}, {"anotherValue", "test2"}}); + auto filteredExpected = expected; + filteredExpected["nested"].erase("a"); + + SETUP_TESTCASES() + } + + SECTION("for array") + { + const std::string nested_type_json_str = R"(["a", "test", 45])"; + const std::string root_type_json_str = R"({ "nested": )" + nested_type_json_str + R"(, "anotherValue": "test" })"; + auto expected = json({{"nested", {"a", "test", 45}}, {"anotherValue", "test"}}); + auto filteredExpected = expected; + filteredExpected["nested"] = json({"test", 45}); + SETUP_TESTCASES() + } + + SECTION("for array with objects") + { + const std::string nested_type_json_str = R"([{"a": 1, "b": "test"}, {"c": 2, "d": "test2"}])"; + const std::string root_type_json_str = R"({ "nested": )" + nested_type_json_str + R"(, "anotherValue": "test" })"; + auto expected = json({{"nested", {{{"a", 1}, {"b", "test"}}, {{"c", 2}, {"d", "test2"}}}}, {"anotherValue", "test"}}); + auto filteredExpected = expected; + filteredExpected["nested"][0].erase("a"); + SETUP_TESTCASES() + + auto j = json::parse(root_type_json_str); + auto nested_array = j["nested"]; + const auto& nested_obj = nested_array[0]; + CHECK(nested_type_json_str.substr(1, 21) == root_type_json_str.substr(nested_obj.start_pos(), nested_obj.end_pos() - nested_obj.start_pos())); + CHECK(nested_type_json_str.substr(24, 22) == root_type_json_str.substr(nested_array[1].start_pos(), nested_array[1].end_pos() - nested_array[1].start_pos())); + } + + SECTION("for two levels of nesting objects") + { + const std::string nested_type_json_str = R"({"nested2": {"b": "test"}})"; + const std::string root_type_json_str = R"({ "a": 2, "nested": )" + nested_type_json_str + R"(, "anotherValue": "test" })"; + auto expected = json({{"a", 2}, {"nested", {{"nested2", {{"b", "test"}}}}}, {"anotherValue", "test"}}); + auto filteredExpected = expected; + filteredExpected.erase("a"); + SETUP_TESTCASES() + + auto j = json::parse(root_type_json_str); + auto nested_obj = j["nested"]["nested2"]; + CHECK(nested_type_json_str.substr(12, 13) == root_type_json_str.substr(nested_obj.start_pos(), nested_obj.end_pos() - nested_obj.start_pos())); + } + + SECTION("for simple types") + { + SECTION("no nested") + { + SECTION("with callback") + { + json::parser_callback_t const cb = [](int /*unused*/, json::parse_event_t /*unused*/, json& /*unused*/) noexcept + { + return true; + }; + + // 1. string type + std::string json_str = R"("test")"; + auto j = json::parse(json_str, cb); + validate_generated_json_and_start_end_pos_helper(json_str, j, "test"); + + // 2. number type + json_str = R"(1)"; + j = json::parse(json_str, cb); + validate_generated_json_and_start_end_pos_helper(json_str, j, 1); + + // 3. boolean type + json_str = R"(true)"; + j = json::parse(json_str, cb); + validate_generated_json_and_start_end_pos_helper(json_str, j, true); + + // 4. null type + json_str = R"(null)"; + j = json::parse(json_str, cb); + validate_generated_json_and_start_end_pos_helper(json_str, j, nullptr); + } + + SECTION("without callback") + { + // 1. string type + std::string json_str = R"("test")"; + auto j = json::parse(json_str); + validate_generated_json_and_start_end_pos_helper(json_str, j, "test"); + + // 2. number type + json_str = R"(1)"; + j = json::parse(json_str); + validate_generated_json_and_start_end_pos_helper(json_str, j, 1); + + json_str = R"(1.001239923)"; + j = json::parse(json_str); + validate_generated_json_and_start_end_pos_helper(json_str, j, 1.001239923); + + json_str = R"(1.123812389000000)"; + j = json::parse(json_str); + validate_generated_json_and_start_end_pos_helper(json_str, j, 1.123812389); + + // 3. boolean type + json_str = R"(true)"; + j = json::parse(json_str); + validate_generated_json_and_start_end_pos_helper(json_str, j, true); + + json_str = R"(false)"; + j = json::parse(json_str); + validate_generated_json_and_start_end_pos_helper(json_str, j, false); + + // 4. null type + json_str = R"(null)"; + j = json::parse(json_str); + validate_generated_json_and_start_end_pos_helper(json_str, j, nullptr); + } + } + + SECTION("string type") + { + const std::string nested_type_json_str = R"("test")"; + const std::string root_type_json_str = R"({ "a": 1, "nested": )" + nested_type_json_str + R"(, "anotherValue": "test" })"; + auto expected = json({{"nested", "test"}, {"anotherValue", "test"}, {"a", 1}}); + auto filteredExpected = expected; + filteredExpected.erase("a"); + SETUP_TESTCASES() + } + + SECTION("number type") + { + const std::string nested_type_json_str = R"(2)"; + const std::string root_type_json_str = R"({ "a": 1, "nested": )" + nested_type_json_str + R"(, "anotherValue": "test" })"; + auto expected = json({{"nested", 2}, {"anotherValue", "test"}, {"a", 1}}); + auto filteredExpected = expected; + filteredExpected.erase("a"); + SETUP_TESTCASES() + } + + SECTION("boolean type") + { + const std::string nested_type_json_str = R"(true)"; + const std::string root_type_json_str = R"({ "a": 1, "nested": )" + nested_type_json_str + R"(, "anotherValue": "test" })"; + auto expected = json({{"nested", true}, {"anotherValue", "test"}, {"a", 1}}); + auto filteredExpected = expected; + filteredExpected.erase("a"); + SETUP_TESTCASES() + } + + SECTION("null type") + { + const std::string nested_type_json_str = R"(null)"; + const std::string root_type_json_str = R"({ "a": 1, "nested": )" + nested_type_json_str + R"(, "anotherValue": "test" })"; + auto expected = json({{"nested", nullptr}, {"anotherValue", "test"}, {"a", 1}}); + auto filteredExpected = expected; + filteredExpected.erase("a"); + SETUP_TESTCASES() + } + } + SECTION("with leading whitespace and newlines around root JSON") + { + const std::string initial_whitespace = R"( + + )"; + const std::string nested_type_json_str = R"({ + "a": 1, + "nested": { + "b": "test" + }, + "anotherValue": "test" + })"; + const std::string end_whitespace = R"( + + )"; + const std::string root_type_json_str = initial_whitespace + nested_type_json_str + end_whitespace; + + auto expected = json({{"a", 1}, {"nested", {{"b", "test"}}}, {"anotherValue", "test"}}); + + auto j = json::parse(root_type_json_str); + + // 2. Check if the generated JSON is as expected + CHECK(j == expected); + + // 3. Check if the start and end positions do not include the surrounding whitespace + CHECK(j.start_pos() == initial_whitespace.size()); + CHECK(j.end_pos() == root_type_json_str.size() - end_whitespace.size()); + } + } +} diff --git a/tests/src/unit-comparison.cpp b/tests/src/unit-comparison.cpp index 7df20486df..befaa04042 100644 --- a/tests/src/unit-comparison.cpp +++ b/tests/src/unit-comparison.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT // cmake/test.cmake selects the C++ standard versions with which to build a @@ -576,7 +576,7 @@ TEST_CASE("lexicographical comparison operators") [1,2,[3,4,5],4,5] )"; - json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t /*unused*/, const json & j) noexcept + const json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t /*unused*/, const json & j) noexcept { // filter all number(2) elements return j != json(2); @@ -584,7 +584,7 @@ TEST_CASE("lexicographical comparison operators") CHECK (j_object == json({{"bar", {{"baz", 1}}}})); - json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t /*unused*/, const json & j) noexcept + const json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t /*unused*/, const json & j) noexcept { return j != json(2); }); diff --git a/tests/src/unit-concepts.cpp b/tests/src/unit-concepts.cpp index e042991845..10153ffc4c 100644 --- a/tests/src/unit-concepts.cpp +++ b/tests/src/unit-concepts.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -20,7 +20,6 @@ TEST_CASE("concepts") // a, b: values of type X: json // TABLE 96 - Container Requirements - // X::value_type must return T CHECK((std::is_same::value)); diff --git a/tests/src/unit-constructor1.cpp b/tests/src/unit-constructor1.cpp index bbd5760575..9a2d17ea04 100644 --- a/tests/src/unit-constructor1.cpp +++ b/tests/src/unit-constructor1.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -412,7 +412,7 @@ TEST_CASE("constructors") SECTION("char[]") { - char const s[] {"Hello world"}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + const char s[] {"Hello world"}; // NOLINT(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) json const j(s); CHECK(j.type() == json::value_t::string); CHECK(j == j_reference); @@ -779,7 +779,7 @@ TEST_CASE("constructors") SECTION("integer literal with u suffix") { - json j(42u); + const json j(42u); CHECK(j.type() == json::value_t::number_unsigned); CHECK(j == j_unsigned_reference); } @@ -793,7 +793,7 @@ TEST_CASE("constructors") SECTION("integer literal with ul suffix") { - json j(42ul); + const json j(42ul); CHECK(j.type() == json::value_t::number_unsigned); CHECK(j == j_unsigned_reference); } @@ -807,7 +807,7 @@ TEST_CASE("constructors") SECTION("integer literal with ull suffix") { - json j(42ull); + const json j(42ull); CHECK(j.type() == json::value_t::number_unsigned); CHECK(j == j_unsigned_reference); } @@ -1362,7 +1362,7 @@ TEST_CASE("constructors") { { json jarray = {1, 2, 3, 4, 5}; - json j_new(jarray.begin(), jarray.begin()); + const json j_new(jarray.begin(), jarray.begin()); CHECK(j_new == json::array()); } { diff --git a/tests/src/unit-constructor2.cpp b/tests/src/unit-constructor2.cpp index 85a91ff9b3..a2c7e19bfe 100644 --- a/tests/src/unit-constructor2.cpp +++ b/tests/src/unit-constructor2.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -17,63 +17,63 @@ TEST_CASE("other constructors and destructor") { SECTION("object") { - json j {{"foo", 1}, {"bar", false}}; + const json j {{"foo", 1}, {"bar", false}}; json k(j); // NOLINT(performance-unnecessary-copy-initialization) CHECK(j == k); } SECTION("array") { - json j {"foo", 1, 42.23, false}; + const json j {"foo", 1, 42.23, false}; json k(j); // NOLINT(performance-unnecessary-copy-initialization) CHECK(j == k); } SECTION("null") { - json j(nullptr); + const json j(nullptr); json k(j); // NOLINT(performance-unnecessary-copy-initialization) CHECK(j == k); } SECTION("boolean") { - json j(true); + const json j(true); json k(j); // NOLINT(performance-unnecessary-copy-initialization) CHECK(j == k); } SECTION("string") { - json j("Hello world"); + const json j("Hello world"); json k(j); // NOLINT(performance-unnecessary-copy-initialization) CHECK(j == k); } SECTION("number (integer)") { - json j(42); + const json j(42); json k(j); // NOLINT(performance-unnecessary-copy-initialization) CHECK(j == k); } SECTION("number (unsigned)") { - json j(42u); + const json j(42u); json k(j); // NOLINT(performance-unnecessary-copy-initialization) CHECK(j == k); } SECTION("number (floating-point)") { - json j(42.23); + const json j(42.23); json k(j); // NOLINT(performance-unnecessary-copy-initialization) CHECK(j == k); } SECTION("binary") { - json j = json::binary({1, 2, 3}); + const json j = json::binary({1, 2, 3}); json k(j); // NOLINT(performance-unnecessary-copy-initialization) CHECK(j == k); } @@ -92,7 +92,7 @@ TEST_CASE("other constructors and destructor") { SECTION("object") { - json j {{"foo", 1}, {"bar", false}}; + const json j {{"foo", 1}, {"bar", false}}; json k; k = j; CHECK(j == k); @@ -100,7 +100,7 @@ TEST_CASE("other constructors and destructor") SECTION("array") { - json j {"foo", 1, 42.23, false}; + const json j {"foo", 1, 42.23, false}; json k; k = j; CHECK(j == k); @@ -108,7 +108,7 @@ TEST_CASE("other constructors and destructor") SECTION("null") { - json j(nullptr); + const json j(nullptr); json k; k = j; CHECK(j == k); @@ -116,7 +116,7 @@ TEST_CASE("other constructors and destructor") SECTION("boolean") { - json j(true); + const json j(true); json k; k = j; CHECK(j == k); @@ -124,7 +124,7 @@ TEST_CASE("other constructors and destructor") SECTION("string") { - json j("Hello world"); + const json j("Hello world"); json k; k = j; CHECK(j == k); @@ -132,7 +132,7 @@ TEST_CASE("other constructors and destructor") SECTION("number (integer)") { - json j(42); + const json j(42); json k; k = j; CHECK(j == k); @@ -140,7 +140,7 @@ TEST_CASE("other constructors and destructor") SECTION("number (unsigned)") { - json j(42u); + const json j(42u); json k; k = j; CHECK(j == k); @@ -148,7 +148,7 @@ TEST_CASE("other constructors and destructor") SECTION("number (floating-point)") { - json j(42.23); + const json j(42.23); json k; k = j; CHECK(j == k); @@ -156,7 +156,7 @@ TEST_CASE("other constructors and destructor") SECTION("binary") { - json j = json::binary({1, 2, 3}); + const json j = json::binary({1, 2, 3}); json k; k = j; CHECK(j == k); diff --git a/tests/src/unit-convenience.cpp b/tests/src/unit-convenience.cpp index 6b50cba0a4..f5104855f0 100644 --- a/tests/src/unit-convenience.cpp +++ b/tests/src/unit-convenience.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -179,9 +179,9 @@ TEST_CASE("convenience functions") SECTION("std::string") { - std::string str1 = concat(hello_iter, world, '!'); - std::string str2 = concat(hello_data, world, '!'); - std::string str3 = concat("Hello, ", world, '!'); + const std::string str1 = concat(hello_iter, world, '!'); + const std::string str2 = concat(hello_data, world, '!'); + const std::string str3 = concat("Hello, ", world, '!'); CHECK(str1 == expected); CHECK(str2 == expected); @@ -190,14 +190,14 @@ TEST_CASE("convenience functions") SECTION("alt_string_iter") { - alt_string_iter str = concat(hello_iter, world, '!'); + const alt_string_iter str = concat(hello_iter, world, '!'); CHECK(str.impl == expected); } SECTION("alt_string_data") { - alt_string_data str = concat(hello_data, world, '!'); + const alt_string_data str = concat(hello_data, world, '!'); CHECK(str.impl == expected); } diff --git a/tests/src/unit-conversions.cpp b/tests/src/unit-conversions.cpp index d7df0bee55..81b8608fb8 100644 --- a/tests/src/unit-conversions.cpp +++ b/tests/src/unit-conversions.cpp @@ -1,10 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// Copyright (c) 2013-2022 Niels Lohmann . -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT // cmake/test.cmake selects the C++ standard versions with which to build a @@ -16,6 +15,11 @@ #include "doctest_compatibility.h" +// skip tests if JSON_DisableEnumSerialization=ON (#4384) +#if defined(JSON_DISABLE_ENUM_SERIALIZATION) && (JSON_DISABLE_ENUM_SERIALIZATION == 1) + #define SKIP_TESTS_FOR_ENUM_SERIALIZATION +#endif + #define JSON_TESTS_PRIVATE #include using nlohmann::json; @@ -32,6 +36,25 @@ using nlohmann::json; DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors") +#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 +#endif + +#ifdef JSON_HAS_CPP_17 + #if __has_include() + #include + #elif __has_include() + #include + #endif +#endif + +#if defined(JSON_HAS_CPP_17) + #include +#endif + TEST_CASE("value conversion") { SECTION("get an object (explicit)") @@ -153,6 +176,7 @@ TEST_CASE("value conversion") } #if JSON_USE_IMPLICIT_CONVERSIONS + SECTION("get an object (implicit)") { const json::object_t o_reference = {{"object", json::object()}, @@ -246,8 +270,8 @@ TEST_CASE("value conversion") SECTION("built-in arrays") { - const char str[] = "a string"; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - const int nbs[] = {0, 1, 2}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + const char str[] = "a string"; // NOLINT(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + const int nbs[] = {0, 1, 2}; // NOLINT(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) const json j2 = nbs; const json j3 = str; @@ -336,7 +360,7 @@ TEST_CASE("value conversion") SECTION("built-in arrays") { - const int nbs[] = {0, 1, 2}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + const int nbs[] = {0, 1, 2}; // NOLINT(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) int nbs2[] = {0, 0, 0}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) const json j2 = nbs; @@ -344,6 +368,68 @@ TEST_CASE("value conversion") CHECK(std::equal(std::begin(nbs), std::end(nbs), std::begin(nbs2))); } + SECTION("built-in arrays: 2D") + { + const int nbs[][3] = {{0, 1, 2}, {3, 4, 5}}; // NOLINT(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + int nbs2[][3] = {{0, 0, 0}, {0, 0, 0}}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + + const json j2 = nbs; + j2.get_to(nbs2); + CHECK(std::equal(std::begin(nbs[0]), std::end(nbs[1]), std::begin(nbs2[0]))); + } + + SECTION("built-in arrays: 3D") + { + // NOLINTBEGIN(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + const int nbs[][2][3] = {\ + {{0, 1, 2}, {3, 4, 5}}, \ + {{10, 11, 12}, {13, 14, 15}}\ + }; + int nbs2[][2][3] = {\ + {{0, 0, 0}, {0, 0, 0}}, \ + {{0, 0, 0}, {0, 0, 0}}\ + }; + // NOLINTEND(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + + const json j2 = nbs; + j2.get_to(nbs2); + CHECK(std::equal(std::begin(nbs[0][0]), std::end(nbs[1][1]), std::begin(nbs2[0][0]))); + } + + SECTION("built-in arrays: 4D") + { + // NOLINTBEGIN(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + const int nbs[][2][2][3] = {\ + { + \ + {{0, 1, 2}, {3, 4, 5}}, \ + {{10, 11, 12}, {13, 14, 15}}\ + }, \ + { + \ + {{20, 21, 22}, {23, 24, 25}}, \ + {{30, 31, 32}, {33, 34, 35}}\ + }\ + }; + int nbs2[][2][2][3] = {\ + { + \ + {{0, 0, 0}, {0, 0, 0}}, \ + {{0, 0, 0}, {0, 0, 0}}\ + }, \ + { + \ + {{0, 0, 0}, {0, 0, 0}}, \ + {{0, 0, 0}, {0, 0, 0}}\ + }\ + }; + // NOLINTEND(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + + const json j2 = nbs; + j2.get_to(nbs2); + CHECK(std::equal(std::begin(nbs[0][0][0]), std::end(nbs[1][1][1]), std::begin(nbs2[0][0][0]))); + } + SECTION("std::deque") { std::deque a{"previous", "value"}; @@ -1164,7 +1250,7 @@ TEST_CASE("value conversion") SECTION("non-const") { - const json j_const = j; + const json j_const = j; // NOLINT(performance-unnecessary-copy-initialization) const auto& b = j_const.get_binary(); CHECK(*json(b).m_data.m_value.binary == *j.m_data.m_value.binary); } @@ -1263,14 +1349,16 @@ TEST_CASE("value conversion") } #endif +#ifndef SKIP_TESTS_FOR_ENUM_SERIALIZATION SECTION("get an enum") { - enum c_enum { value_1, value_2 }; + enum c_enum { value_1, value_2 }; // NOLINT(cppcoreguidelines-use-enum-class) enum class cpp_enum { value_1, value_2 }; CHECK(json(value_1).get() == value_1); CHECK(json(cpp_enum::value_1).get() == cpp_enum::value_1); } +#endif SECTION("more involved conversions") { @@ -1426,7 +1514,7 @@ TEST_CASE("value conversion") SECTION("std::map (array of pairs)") { - std::map m{{0, 1}, {1, 2}, {2, 3}}; + const std::map m{{0, 1}, {1, 2}, {2, 3}}; json const j6 = m; auto m2 = j6.get>(); @@ -1451,7 +1539,7 @@ TEST_CASE("value conversion") SECTION("std::unordered_map (array of pairs)") { - std::unordered_map m{{0, 1}, {1, 2}, {2, 3}}; + const std::unordered_map m{{0, 1}, {1, 2}, {2, 3}}; json const j6 = m; auto m2 = j6.get>(); @@ -1503,7 +1591,7 @@ TEST_CASE("value conversion") enum class cards {kreuz, pik, herz, karo}; -// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive +// NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive NLOHMANN_JSON_SERIALIZE_ENUM(cards, { {cards::kreuz, "kreuz"}, @@ -1513,7 +1601,7 @@ NLOHMANN_JSON_SERIALIZE_ENUM(cards, {cards::karo, "karo"} }) -enum TaskState +enum TaskState // NOLINT(cert-int09-c,readability-enum-initial-value,cppcoreguidelines-use-enum-class) { TS_STOPPED, TS_RUNNING, @@ -1521,7 +1609,7 @@ enum TaskState TS_INVALID = -1, }; -// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive +// NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive NLOHMANN_JSON_SERIALIZE_ENUM(TaskState, { {TS_INVALID, nullptr}, @@ -1569,4 +1657,97 @@ TEST_CASE("JSON to enum mapping") } } +#ifdef JSON_HAS_CPP_17 +#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM +TEST_CASE("std::filesystem::path") +{ + SECTION("ascii") + { + json const j_string = "Path"; + auto p = j_string.template get(); + json const j_path = p; + + CHECK(j_path.template get() == + j_string.template get()); + } + + SECTION("utf-8") + { + json const j_string = "P\xc4\x9b\xc5\xa1ina"; + auto p = j_string.template get(); + json const j_path = p; + + CHECK(j_path.template get() == + j_string.template get()); + } +} +#endif + +#ifndef JSON_USE_IMPLICIT_CONVERSIONS +TEST_CASE("std::optional") +{ + SECTION("null") + { + json j_null; + std::optional opt_null; + + CHECK(json(opt_null) == j_null); + CHECK(j_null.get>() == std::nullopt); + } + + SECTION("string") + { + json j_string = "string"; + std::optional opt_string = "string"; + + CHECK(json(opt_string) == j_string); + CHECK(std::optional(j_string) == opt_string); + } + + SECTION("bool") + { + json j_bool = true; + std::optional opt_bool = true; + + CHECK(json(opt_bool) == j_bool); + CHECK(std::optional(j_bool) == opt_bool); + } + + SECTION("number") + { + json j_number = 1; + std::optional opt_int = 1; + + CHECK(json(opt_int) == j_number); + CHECK(j_number.get>() == opt_int); + } + + SECTION("array") + { + json j_array = {1, 2, nullptr}; + std::vector> opt_array = {{1, 2, std::nullopt}}; + + CHECK(json(opt_array) == j_array); + CHECK(j_array.get>>() == opt_array); + } + + SECTION("object") + { + json j_object = {{"one", 1}, {"two", 2}, {"zero", nullptr}}; + std::map> opt_object {{"one", 1}, {"two", 2}, {"zero", std::nullopt}}; + + CHECK(json(opt_object) == j_object); + CHECK(std::map>(j_object) == opt_object); + } +} +#endif +#endif + +#ifdef JSON_HAS_CPP_17 + #undef JSON_HAS_CPP_17 +#endif + +#ifdef JSON_HAS_CPP_14 + #undef JSON_HAS_CPP_14 +#endif DOCTEST_CLANG_SUPPRESS_WARNING_POP diff --git a/tests/src/unit-custom-base-class.cpp b/tests/src/unit-custom-base-class.cpp index 7d3c2eefc4..7dab5c5766 100644 --- a/tests/src/unit-custom-base-class.cpp +++ b/tests/src/unit-custom-base-class.cpp @@ -1,10 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// Copyright (c) 2013-2019 Niels Lohmann . -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include @@ -283,8 +282,8 @@ TEST_CASE("JSON Visit Node") }; json.visit( - [&](const json_with_visitor_t::json_pointer & p, - const json_with_visitor_t& j) + [&](const json_with_visitor_t::json_pointer & p, + const json_with_visitor_t& j) { std::stringstream str; str << p.to_string() << " - " ; @@ -331,6 +330,6 @@ TEST_CASE("JSON Visit Node") CHECK(expected.count(str.str()) == 1); expected.erase(str.str()); } - ); + ); CHECK(expected.empty()); } diff --git a/tests/src/unit-deserialization.cpp b/tests/src/unit-deserialization.cpp index 65bb28a224..0ade1ad52c 100644 --- a/tests/src/unit-deserialization.cpp +++ b/tests/src/unit-deserialization.cpp @@ -1,10 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// Copyright (c) 2013-2022 Niels Lohmann . -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -20,6 +19,11 @@ using nlohmann::json; #include #include +#if defined(_WIN32) + #define NOMINMAX + #include // for GetACP() +#endif + namespace { struct SaxEventLogger : public nlohmann::json_sax @@ -77,7 +81,7 @@ struct SaxEventLogger : public nlohmann::json_sax bool start_object(std::size_t elements) override { - if (elements == static_cast(-1)) + if (elements == (std::numeric_limits::max)()) { events.emplace_back("start_object()"); } @@ -102,7 +106,7 @@ struct SaxEventLogger : public nlohmann::json_sax bool start_array(std::size_t elements) override { - if (elements == static_cast(-1)) + if (elements == (std::numeric_limits::max)()) { events.emplace_back("start_array()"); } @@ -132,7 +136,7 @@ struct SaxEventLoggerExitAfterStartObject : public SaxEventLogger { bool start_object(std::size_t elements) override { - if (elements == static_cast(-1)) + if (elements == (std::numeric_limits::max)()) { events.emplace_back("start_object()"); } @@ -157,7 +161,7 @@ struct SaxEventLoggerExitAfterStartArray : public SaxEventLogger { bool start_array(std::size_t elements) override { - if (elements == static_cast(-1)) + if (elements == (std::numeric_limits::max)()) { events.emplace_back("start_array()"); } @@ -214,6 +218,20 @@ class proxy_iterator private: iterator* m_it = nullptr; }; + +// JSON_HAS_CPP_20 +#if defined(__cpp_char8_t) +bool check_utf8() +{ +#if defined(_WIN32) + // Runtime check of the active ANSI code page + // 65001 == UTF-8 + return GetACP() == 65001; +#else + return true; +#endif +} +#endif } // namespace TEST_CASE("deserialization") @@ -228,7 +246,7 @@ TEST_CASE("deserialization") ss1 << R"(["foo",1,2,3,false,{"one":1}])"; ss2 << R"(["foo",1,2,3,false,{"one":1}])"; ss3 << R"(["foo",1,2,3,false,{"one":1}])"; - json j = json::parse(ss1); + const json j = json::parse(ss1); CHECK(json::accept(ss2)); CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); @@ -247,7 +265,7 @@ TEST_CASE("deserialization") SECTION("string literal") { const auto* s = R"(["foo",1,2,3,false,{"one":1}])"; - json j = json::parse(s); + const json j = json::parse(s); CHECK(json::accept(s)); CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); @@ -266,7 +284,7 @@ TEST_CASE("deserialization") SECTION("string_t") { json::string_t const s = R"(["foo",1,2,3,false,{"one":1}])"; - json j = json::parse(s); + const json j = json::parse(s); CHECK(json::accept(s)); CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); @@ -360,6 +378,10 @@ TEST_CASE("deserialization") "start_object()", "key(one)", "number_unsigned(1)", "end_object()", "parse_error(29)" })); + + const char* string = nullptr; + CHECK_THROWS_WITH_AS(_ = json::parse(string), "[json.exception.parse_error.101] parse error: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::parse(nullptr), "[json.exception.parse_error.101] parse error: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error&); } SECTION("operator<<") @@ -583,7 +605,7 @@ TEST_CASE("deserialization") auto first = str.begin(); auto last = str.end(); json j; - json_sax_dom_parser sax(j, true); + json_sax_dom_parser sax(j, true); CHECK(json::sax_parse(proxy(first), proxy(last), &sax, input_format_t::json, false)); @@ -1129,15 +1151,43 @@ TEST_CASE("deserialization") CHECK(object_count == 4); } } + + // build with C++20 + // JSON_HAS_CPP_20 +#if defined(__cpp_char8_t) + SECTION("Using _json with char8_t literals #4945") + { + // Regular narrow string literal + const auto j1 = R"({"key": "value", "num": 42})"_json; + CHECK(j1["key"] == "value"); + CHECK(j1["num"] == 42); + + // UTF-8 prefixed literal (C++20 and later); + // MSVC may not set /utf-8, so we need to check + if (check_utf8()) + { + const auto j2 = u8R"({"emoji": "😀", "msg": "hello"})"_json; + CHECK(j2["emoji"] == "😀"); + CHECK(j2["msg"] == "hello"); + } + + const auto j3 = u8R"({"key": "value", "num": 42})"_json; + CHECK(j3["key"] == "value"); + CHECK(j3["num"] == 42); + } +#endif } -TEST_CASE_TEMPLATE("deserialization of different character types (ASCII)", T, - char, unsigned char, signed char, - wchar_t, - char16_t, char32_t, - std::uint8_t, std::int8_t, - std::int16_t, std::uint16_t, - std::int32_t, std::uint32_t) +// select the types to test - char8_t is only available since C++20 if and only +// if __cpp_char8_t is defined. +#define TYPE_LIST(...) __VA_ARGS__ +#if defined(__cpp_char8_t) && (__cpp_char8_t >= 201811L) + #define ASCII_TYPES TYPE_LIST(char, wchar_t, char16_t, char32_t, char8_t) +#else + #define ASCII_TYPES TYPE_LIST(char, wchar_t, char16_t, char32_t) +#endif + +TEST_CASE_TEMPLATE("deserialization of different character types (ASCII)", T, ASCII_TYPES) // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) { std::vector const v = {'t', 'r', 'u', 'e'}; CHECK(json::parse(v) == json(true)); @@ -1149,8 +1199,7 @@ TEST_CASE_TEMPLATE("deserialization of different character types (ASCII)", T, CHECK(l.events == std::vector({"boolean(true)"})); } -TEST_CASE_TEMPLATE("deserialization of different character types (UTF-8)", T, - char, unsigned char, std::uint8_t) +TEST_CASE_TEMPLATE("deserialization of different character types (UTF-8)", T, char, unsigned char, std::uint8_t) // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) { // a star emoji std::vector const v = {'"', static_cast(0xe2u), static_cast(0xadu), static_cast(0x90u), static_cast(0xefu), static_cast(0xb8u), static_cast(0x8fu), '"'}; @@ -1162,8 +1211,7 @@ TEST_CASE_TEMPLATE("deserialization of different character types (UTF-8)", T, CHECK(l.events.size() == 1); } -TEST_CASE_TEMPLATE("deserialization of different character types (UTF-16)", T, - char16_t, std::uint16_t) +TEST_CASE_TEMPLATE("deserialization of different character types (UTF-16)", T, char16_t) // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) { // a star emoji std::vector const v = {static_cast('"'), static_cast(0x2b50), static_cast(0xfe0f), static_cast('"')}; @@ -1175,8 +1223,7 @@ TEST_CASE_TEMPLATE("deserialization of different character types (UTF-16)", T, CHECK(l.events.size() == 1); } -TEST_CASE_TEMPLATE("deserialization of different character types (UTF-32)", T, - char32_t, std::uint32_t) +TEST_CASE_TEMPLATE("deserialization of different character types (UTF-32)", T, char32_t) // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) { // a star emoji std::vector const v = {static_cast('"'), static_cast(0x2b50), static_cast(0xfe0f), static_cast('"')}; diff --git a/tests/src/unit-diagnostic-positions-only.cpp b/tests/src/unit-diagnostic-positions-only.cpp new file mode 100644 index 0000000000..735376514a --- /dev/null +++ b/tests/src/unit-diagnostic-positions-only.cpp @@ -0,0 +1,44 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ (supporting code) +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + +#include "doctest_compatibility.h" + +#ifdef JSON_DIAGNOSTICS + #undef JSON_DIAGNOSTICS +#endif + +#define JSON_DIAGNOSTICS 0 +#define JSON_DIAGNOSTIC_POSITIONS 1 +#include + +using json = nlohmann::json; + +TEST_CASE("Better diagnostics with positions only") +{ + SECTION("invalid type") + { + const std::string json_invalid_string = R"( + { + "address": { + "street": "Fake Street", + "housenumber": "1" + } + } + )"; + json j = json::parse(json_invalid_string); + CHECK_THROWS_WITH_AS(j.at("address").at("housenumber").get(), + "[json.exception.type_error.302] (bytes 108-111) type must be number, but is string", json::type_error); + } + + SECTION("invalid type without positions") + { + const json j = "foo"; + CHECK_THROWS_WITH_AS(j.get(), + "[json.exception.type_error.302] type must be number, but is string", json::type_error); + } +} diff --git a/tests/src/unit-diagnostic-positions.cpp b/tests/src/unit-diagnostic-positions.cpp new file mode 100644 index 0000000000..e6e9752df0 --- /dev/null +++ b/tests/src/unit-diagnostic-positions.cpp @@ -0,0 +1,40 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ (supporting code) +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + +#include "doctest_compatibility.h" + +#define JSON_DIAGNOSTICS 1 +#define JSON_DIAGNOSTIC_POSITIONS 1 +#include + +using json = nlohmann::json; + +TEST_CASE("Better diagnostics with positions") +{ + SECTION("invalid type") + { + const std::string json_invalid_string = R"( + { + "address": { + "street": "Fake Street", + "housenumber": "1" + } + } + )"; + json j = json::parse(json_invalid_string); + CHECK_THROWS_WITH_AS(j.at("address").at("housenumber").get(), + "[json.exception.type_error.302] (/address/housenumber) (bytes 108-111) type must be number, but is string", json::type_error); + } + + SECTION("invalid type without positions") + { + const json j = "foo"; + CHECK_THROWS_WITH_AS(j.get(), + "[json.exception.type_error.302] type must be number, but is string", json::type_error); + } +} diff --git a/tests/src/unit-diagnostics.cpp b/tests/src/unit-diagnostics.cpp index 0e172b915e..c5feafe442 100644 --- a/tests/src/unit-diagnostics.cpp +++ b/tests/src/unit-diagnostics.cpp @@ -1,10 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// Copyright (c) 2013-2022 Niels Lohmann . -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -243,4 +242,24 @@ TEST_CASE("Regression tests for extended diagnostics") json const j_arr_copy = j_arr; } } + + SECTION("Regression test for issue #3915 - JSON_DIAGNOSTICS trigger assertion") + { + json j = json::object(); + j["root"] = "root_str"; + + json jj = json::object(); + jj["child"] = json::object(); + + // If do not push anything in object, then no assert will be produced + jj["child"]["prop1"] = "prop1_value"; + + // Push all properties of child in parent + j.insert(jj.at("child").begin(), jj.at("child").end()); + + // Here assert is generated when construct new json + const json k(j); + + CHECK(k.dump() == "{\"prop1\":\"prop1_value\",\"root\":\"root_str\"}"); + } } diff --git a/tests/src/unit-disabled_exceptions.cpp b/tests/src/unit-disabled_exceptions.cpp index 4ad155140a..e4532e2349 100644 --- a/tests/src/unit-disabled_exceptions.cpp +++ b/tests/src/unit-disabled_exceptions.cpp @@ -1,10 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// Copyright (c) 2013-2022 Niels Lohmann . -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -20,10 +19,10 @@ using json = nlohmann::json; // for #2824 ///////////////////////////////////////////////////////////////////// -class sax_no_exception : public nlohmann::detail::json_sax_dom_parser +class sax_no_exception : public nlohmann::detail::json_sax_dom_parser { public: - explicit sax_no_exception(json& j) : nlohmann::detail::json_sax_dom_parser(j, false) {} + explicit sax_no_exception(json& j) : nlohmann::detail::json_sax_dom_parser(j, false) {} static bool parse_error(std::size_t /*position*/, const std::string& /*last_token*/, const json::exception& ex) { diff --git a/tests/src/unit-element_access1.cpp b/tests/src/unit-element_access1.cpp index 55a07ede14..eccefb3ce7 100644 --- a/tests/src/unit-element_access1.cpp +++ b/tests/src/unit-element_access1.cpp @@ -1,10 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// Copyright (c) 2013-2022 Niels Lohmann . -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -17,7 +16,7 @@ TEST_CASE("element access 1") SECTION("array") { json j = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - const json j_const = j; + const json j_const = j; // NOLINT(performance-unnecessary-copy-initialization) SECTION("access specified element with bounds checking") { @@ -55,7 +54,7 @@ TEST_CASE("element access 1") SECTION("null") { json j_nonarray(json::value_t::null); - const json j_nonarray_const(j_nonarray); + const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization) CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with null", json::type_error&); CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with null", json::type_error&); @@ -64,7 +63,7 @@ TEST_CASE("element access 1") SECTION("boolean") { json j_nonarray(json::value_t::boolean); - const json j_nonarray_const(j_nonarray); + const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization) CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with boolean", json::type_error&); CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with boolean", json::type_error&); @@ -73,7 +72,7 @@ TEST_CASE("element access 1") SECTION("string") { json j_nonarray(json::value_t::string); - const json j_nonarray_const(j_nonarray); + const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization) CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with string", json::type_error&); CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with string", json::type_error&); @@ -82,7 +81,7 @@ TEST_CASE("element access 1") SECTION("object") { json j_nonarray(json::value_t::object); - const json j_nonarray_const(j_nonarray); + const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization) CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with object", json::type_error&); CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with object", json::type_error&); @@ -91,7 +90,7 @@ TEST_CASE("element access 1") SECTION("number (integer)") { json j_nonarray(json::value_t::number_integer); - const json j_nonarray_const(j_nonarray); + const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization) CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); @@ -100,7 +99,7 @@ TEST_CASE("element access 1") SECTION("number (unsigned)") { json j_nonarray(json::value_t::number_unsigned); - const json j_nonarray_const(j_nonarray); + const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization) CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); @@ -109,7 +108,7 @@ TEST_CASE("element access 1") SECTION("number (floating-point)") { json j_nonarray(json::value_t::number_float); - const json j_nonarray_const(j_nonarray); + const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization) CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); @@ -155,7 +154,7 @@ TEST_CASE("element access 1") SECTION("standard tests") { json j_nonarray(json::value_t::null); - const json j_nonarray_const(j_nonarray); + const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization) CHECK_NOTHROW(j_nonarray[0]); CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with null", json::type_error&); } @@ -171,7 +170,7 @@ TEST_CASE("element access 1") SECTION("boolean") { json j_nonarray(json::value_t::boolean); - const json j_nonarray_const(j_nonarray); + const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization) CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with boolean", json::type_error&); CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with boolean", json::type_error&); } @@ -179,7 +178,7 @@ TEST_CASE("element access 1") SECTION("string") { json j_nonarray(json::value_t::string); - const json j_nonarray_const(j_nonarray); + const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization) CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with string", json::type_error&); CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with string", json::type_error&); } @@ -187,7 +186,7 @@ TEST_CASE("element access 1") SECTION("object") { json j_nonarray(json::value_t::object); - const json j_nonarray_const(j_nonarray); + const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization) CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with object", json::type_error&); CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with object", json::type_error&); } @@ -195,7 +194,7 @@ TEST_CASE("element access 1") SECTION("number (integer)") { json j_nonarray(json::value_t::number_integer); - const json j_nonarray_const(j_nonarray); + const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization) CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&); CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&); } @@ -203,7 +202,7 @@ TEST_CASE("element access 1") SECTION("number (unsigned)") { json j_nonarray(json::value_t::number_unsigned); - const json j_nonarray_const(j_nonarray); + const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization) CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&); CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&); } @@ -211,7 +210,7 @@ TEST_CASE("element access 1") SECTION("number (floating-point)") { json j_nonarray(json::value_t::number_float); - const json j_nonarray_const(j_nonarray); + const json j_nonarray_const(j_nonarray); // NOLINT(performance-unnecessary-copy-initialization) CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&); CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&); } @@ -290,13 +289,13 @@ TEST_CASE("element access 1") { { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::iterator it2 = jarray.erase(jarray.begin(), jarray.end()); + const json::iterator it2 = jarray.erase(jarray.begin(), jarray.end()); CHECK(jarray == json::array()); CHECK(it2 == jarray.end()); } { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cend()); + const json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cend()); CHECK(jarray == json::array()); CHECK(it2 == jarray.cend()); } @@ -538,13 +537,13 @@ TEST_CASE("element access 1") { { json j = "foo"; - json::iterator it = j.erase(j.begin()); + const json::iterator it = j.erase(j.begin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { json j = "bar"; - json::const_iterator it = j.erase(j.cbegin()); + const json::const_iterator it = j.erase(j.cbegin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } @@ -554,13 +553,13 @@ TEST_CASE("element access 1") { { json j = false; - json::iterator it = j.erase(j.begin()); + const json::iterator it = j.erase(j.begin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { json j = true; - json::const_iterator it = j.erase(j.cbegin()); + const json::const_iterator it = j.erase(j.cbegin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } @@ -570,13 +569,13 @@ TEST_CASE("element access 1") { { json j = 17; - json::iterator it = j.erase(j.begin()); + const json::iterator it = j.erase(j.begin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { json j = 17; - json::const_iterator it = j.erase(j.cbegin()); + const json::const_iterator it = j.erase(j.cbegin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } @@ -586,13 +585,13 @@ TEST_CASE("element access 1") { { json j = 17u; - json::iterator it = j.erase(j.begin()); + const json::iterator it = j.erase(j.begin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { json j = 17u; - json::const_iterator it = j.erase(j.cbegin()); + const json::const_iterator it = j.erase(j.cbegin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } @@ -602,13 +601,13 @@ TEST_CASE("element access 1") { { json j = 23.42; - json::iterator it = j.erase(j.begin()); + const json::iterator it = j.erase(j.begin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { json j = 23.42; - json::const_iterator it = j.erase(j.cbegin()); + const json::const_iterator it = j.erase(j.cbegin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } @@ -618,13 +617,13 @@ TEST_CASE("element access 1") { { json j = json::binary({1, 2, 3}); - json::iterator it = j.erase(j.begin()); + const json::iterator it = j.erase(j.begin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { json j = json::binary({1, 2, 3}); - json::const_iterator it = j.erase(j.cbegin()); + const json::const_iterator it = j.erase(j.cbegin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } @@ -712,13 +711,13 @@ TEST_CASE("element access 1") { { json j = "foo"; - json::iterator it = j.erase(j.begin(), j.end()); + const json::iterator it = j.erase(j.begin(), j.end()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { json j = "bar"; - json::const_iterator it = j.erase(j.cbegin(), j.cend()); + const json::const_iterator it = j.erase(j.cbegin(), j.cend()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } @@ -728,13 +727,13 @@ TEST_CASE("element access 1") { { json j = false; - json::iterator it = j.erase(j.begin(), j.end()); + const json::iterator it = j.erase(j.begin(), j.end()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { json j = true; - json::const_iterator it = j.erase(j.cbegin(), j.cend()); + const json::const_iterator it = j.erase(j.cbegin(), j.cend()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } @@ -744,13 +743,13 @@ TEST_CASE("element access 1") { { json j = 17; - json::iterator it = j.erase(j.begin(), j.end()); + const json::iterator it = j.erase(j.begin(), j.end()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { json j = 17; - json::const_iterator it = j.erase(j.cbegin(), j.cend()); + const json::const_iterator it = j.erase(j.cbegin(), j.cend()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } @@ -760,13 +759,13 @@ TEST_CASE("element access 1") { { json j = 17u; - json::iterator it = j.erase(j.begin(), j.end()); + const json::iterator it = j.erase(j.begin(), j.end()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { json j = 17u; - json::const_iterator it = j.erase(j.cbegin(), j.cend()); + const json::const_iterator it = j.erase(j.cbegin(), j.cend()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } @@ -776,13 +775,13 @@ TEST_CASE("element access 1") { { json j = 23.42; - json::iterator it = j.erase(j.begin(), j.end()); + const json::iterator it = j.erase(j.begin(), j.end()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { json j = 23.42; - json::const_iterator it = j.erase(j.cbegin(), j.cend()); + const json::const_iterator it = j.erase(j.cbegin(), j.cend()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } @@ -792,13 +791,13 @@ TEST_CASE("element access 1") { { json j = json::binary({1, 2, 3}); - json::iterator it = j.erase(j.begin(), j.end()); + const json::iterator it = j.erase(j.begin(), j.end()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { json j = json::binary({1, 2, 3}); - json::const_iterator it = j.erase(j.cbegin(), j.cend()); + const json::const_iterator it = j.erase(j.cbegin(), j.cend()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } diff --git a/tests/src/unit-element_access2.cpp b/tests/src/unit-element_access2.cpp index 8497fb944c..9097f4599a 100644 --- a/tests/src/unit-element_access2.cpp +++ b/tests/src/unit-element_access2.cpp @@ -1,10 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// Copyright (c) 2013-2022 Niels Lohmann . -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -17,7 +16,7 @@ // build test with C++14 // JSON_HAS_CPP_14 -TEST_CASE_TEMPLATE("element access 2", Json, nlohmann::json, nlohmann::ordered_json) +TEST_CASE_TEMPLATE("element access 2", Json, nlohmann::json, nlohmann::ordered_json) // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) { SECTION("object") { @@ -83,7 +82,7 @@ TEST_CASE_TEMPLATE("element access 2", Json, nlohmann::json, nlohmann::ordered_j SECTION("null") { Json j_nonobject(Json::value_t::null); - const Json j_nonobject_const(j_nonobject); + const Json j_nonobject_const(j_nonobject); // NOLINT(performance-unnecessary-copy-initialization) CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with null", typename Json::type_error&); CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with null", typename Json::type_error&); @@ -96,7 +95,7 @@ TEST_CASE_TEMPLATE("element access 2", Json, nlohmann::json, nlohmann::ordered_j SECTION("boolean") { Json j_nonobject(Json::value_t::boolean); - const Json j_nonobject_const(j_nonobject); + const Json j_nonobject_const(j_nonobject); // NOLINT(performance-unnecessary-copy-initialization) CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with boolean", typename Json::type_error&); CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with boolean", typename Json::type_error&); @@ -109,7 +108,7 @@ TEST_CASE_TEMPLATE("element access 2", Json, nlohmann::json, nlohmann::ordered_j SECTION("string") { Json j_nonobject(Json::value_t::string); - const Json j_nonobject_const(j_nonobject); + const Json j_nonobject_const(j_nonobject); // NOLINT(performance-unnecessary-copy-initialization) CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with string", typename Json::type_error&); CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with string", typename Json::type_error&); @@ -122,7 +121,7 @@ TEST_CASE_TEMPLATE("element access 2", Json, nlohmann::json, nlohmann::ordered_j SECTION("array") { Json j_nonobject(Json::value_t::array); - const Json j_nonobject_const(j_nonobject); + const Json j_nonobject_const(j_nonobject); // NOLINT(performance-unnecessary-copy-initialization) CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with array", typename Json::type_error&); CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with array", typename Json::type_error&); @@ -135,7 +134,7 @@ TEST_CASE_TEMPLATE("element access 2", Json, nlohmann::json, nlohmann::ordered_j SECTION("number (integer)") { Json j_nonobject(Json::value_t::number_integer); - const Json j_nonobject_const(j_nonobject); + const Json j_nonobject_const(j_nonobject); // NOLINT(performance-unnecessary-copy-initialization) CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with number", typename Json::type_error&); CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with number", typename Json::type_error&); @@ -148,7 +147,7 @@ TEST_CASE_TEMPLATE("element access 2", Json, nlohmann::json, nlohmann::ordered_j SECTION("number (unsigned)") { Json j_nonobject(Json::value_t::number_unsigned); - const Json j_nonobject_const(j_nonobject); + const Json j_nonobject_const(j_nonobject); // NOLINT(performance-unnecessary-copy-initialization) CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with number", typename Json::type_error&); CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with number", typename Json::type_error&); @@ -161,7 +160,7 @@ TEST_CASE_TEMPLATE("element access 2", Json, nlohmann::json, nlohmann::ordered_j SECTION("number (floating-point)") { Json j_nonobject(Json::value_t::number_float); - const Json j_nonobject_const(j_nonobject); + const Json j_nonobject_const(j_nonobject); // NOLINT(performance-unnecessary-copy-initialization) CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with number", typename Json::type_error&); CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with number", typename Json::type_error&); @@ -848,13 +847,13 @@ TEST_CASE_TEMPLATE("element access 2", Json, nlohmann::json, nlohmann::ordered_j { { Json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - typename Json::iterator it2 = jobject.erase(jobject.begin(), jobject.end()); + const typename Json::iterator it2 = jobject.erase(jobject.begin(), jobject.end()); CHECK(jobject == Json::object()); CHECK(it2 == jobject.end()); } { Json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - typename Json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cend()); + const typename Json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cend()); CHECK(jobject == Json::object()); CHECK(it2 == jobject.cend()); } @@ -1456,7 +1455,7 @@ TEST_CASE_TEMPLATE("element access 2", Json, nlohmann::json, nlohmann::ordered_j } #if !defined(JSON_NOEXCEPTION) -TEST_CASE_TEMPLATE("element access 2 (throwing tests)", Json, nlohmann::json, nlohmann::ordered_json) +TEST_CASE_TEMPLATE("element access 2 (throwing tests)", Json, nlohmann::json, nlohmann::ordered_json) // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) { SECTION("object") { @@ -1492,7 +1491,7 @@ TEST_CASE_TEMPLATE("element access 2 (throwing tests)", Json, nlohmann::json, nl #endif // TODO(falbrechtskirchinger) merge with the other test case; clean up -TEST_CASE_TEMPLATE("element access 2 (additional value() tests)", Json, nlohmann::json, nlohmann::ordered_json) +TEST_CASE_TEMPLATE("element access 2 (additional value() tests)", Json, nlohmann::json, nlohmann::ordered_json) // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) { using string_t = typename Json::string_t; using number_integer_t = typename Json::number_integer_t; @@ -1566,9 +1565,9 @@ TEST_CASE_TEMPLATE("element access 2 (additional value() tests)", Json, nlohmann SECTION("const char(&)[] key") { - const char key[] = "foo"; // NOLINT(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) - const char key2[] = "baz"; // NOLINT(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) - const char key_notfound[] = "bar"; // NOLINT(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) + const char key[] = "foo"; // NOLINT(misc-const-correctness,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) + const char key2[] = "baz"; // NOLINT(misc-const-correctness,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) + const char key_notfound[] = "bar"; // NOLINT(misc-const-correctness,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) CHECK(j.value(key, "default") == "bar"); CHECK(j.value(key, cpstr) == "bar"); @@ -1700,9 +1699,9 @@ TEST_CASE_TEMPLATE("element access 2 (additional value() tests)", Json, nlohmann SECTION("const char(&)[] key") { - const char key[] = "foo"; // NOLINT(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) - const char key2[] = "baz"; // NOLINT(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) - const char key_notfound[] = "bar"; // NOLINT(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) + const char key[] = "foo"; // NOLINT(misc-const-correctness,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) + const char key2[] = "baz"; // NOLINT(misc-const-correctness,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) + const char key_notfound[] = "bar"; // NOLINT(misc-const-correctness,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) CHECK(j.template value(key, "default") == "bar"); CHECK(j.template value(key, cpstr) == "bar"); diff --git a/tests/src/unit-hash.cpp b/tests/src/unit-hash.cpp index 7c98a27cbd..c161efa6e9 100644 --- a/tests/src/unit-hash.cpp +++ b/tests/src/unit-hash.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit-inspection.cpp b/tests/src/unit-inspection.cpp index 0574094c5d..c70913d5b1 100644 --- a/tests/src/unit-inspection.cpp +++ b/tests/src/unit-inspection.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -278,8 +278,8 @@ TEST_CASE("object inspection") std::ifstream f_escaped(TEST_DATA_DIRECTORY "/json_nlohmann_tests/all_unicode_ascii.json"); std::ifstream f_unescaped(TEST_DATA_DIRECTORY "/json_nlohmann_tests/all_unicode.json"); - json j1 = json::parse(f_escaped); - json j2 = json::parse(f_unescaped); + const json j1 = json::parse(f_escaped); + const json j2 = json::parse(f_unescaped); CHECK(j1 == j2); } @@ -289,10 +289,10 @@ TEST_CASE("object inspection") std::ifstream f_unescaped(TEST_DATA_DIRECTORY "/json_nlohmann_tests/all_unicode.json"); json const value = json::parse(f_unescaped); - std::string text = value.dump(4, ' ', true); + const std::string text = value.dump(4, ' ', true); - std::string expected((std::istreambuf_iterator(f_escaped)), - std::istreambuf_iterator()); + const std::string expected((std::istreambuf_iterator(f_escaped)), + std::istreambuf_iterator()); CHECK(text == expected); } } @@ -333,7 +333,7 @@ TEST_CASE("object inspection") }) { json const j1 = json::parse(s); - std::string s1 = j1.dump(); + const std::string s1 = j1.dump(); json const j2 = json::parse(s1); std::string s2 = j2.dump(); CHECK(s1 == s2); @@ -396,63 +396,63 @@ TEST_CASE("object inspection") SECTION("null") { json const j = nullptr; - json::value_t t = j; + const json::value_t t = j; CHECK(t == j.type()); } SECTION("object") { json const j = {{"foo", "bar"}}; - json::value_t t = j; + const json::value_t t = j; CHECK(t == j.type()); } SECTION("array") { json const j = {1, 2, 3, 4}; - json::value_t t = j; + const json::value_t t = j; CHECK(t == j.type()); } SECTION("boolean") { json const j = true; - json::value_t t = j; + const json::value_t t = j; CHECK(t == j.type()); } SECTION("string") { json const j = "Hello world"; - json::value_t t = j; + const json::value_t t = j; CHECK(t == j.type()); } SECTION("number (integer)") { json const j = 23; - json::value_t t = j; + const json::value_t t = j; CHECK(t == j.type()); } SECTION("number (unsigned)") { json const j = 23u; - json::value_t t = j; + const json::value_t t = j; CHECK(t == j.type()); } SECTION("number (floating-point)") { json const j = 42.23; - json::value_t t = j; + const json::value_t t = j; CHECK(t == j.type()); } SECTION("binary") { json const j = json::binary({}); - json::value_t t = j; + const json::value_t t = j; CHECK(t == j.type()); } } diff --git a/tests/src/unit-items.cpp b/tests/src/unit-items.cpp index ef2dd74b1f..fa89484471 100644 --- a/tests/src/unit-items.cpp +++ b/tests/src/unit-items.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit-iterators1.cpp b/tests/src/unit-iterators1.cpp index 24669a625b..b7b0aa9eb5 100644 --- a/tests/src/unit-iterators1.cpp +++ b/tests/src/unit-iterators1.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -18,10 +18,10 @@ TEST_CASE("iterators 1") { SECTION("uninitialized") { - json::iterator it; + json::iterator it; // NOLINT(misc-const-correctness) CHECK(it.m_object == nullptr); - json::const_iterator cit; + json::const_iterator cit; // NOLINT(misc-const-correctness) CHECK(cit.m_object == nullptr); } @@ -1498,46 +1498,46 @@ TEST_CASE("iterators 1") SECTION("json + begin/end") { - json::iterator it = j.begin(); + const json::iterator it = j.begin(); CHECK(it == j.end()); } SECTION("const json + begin/end") { - json::const_iterator it_begin = j_const.begin(); + const json::const_iterator it_begin = j_const.begin(); json::const_iterator it_end = j_const.end(); CHECK(it_begin == it_end); } SECTION("json + cbegin/cend") { - json::const_iterator it_begin = j.cbegin(); + const json::const_iterator it_begin = j.cbegin(); json::const_iterator it_end = j.cend(); CHECK(it_begin == it_end); } SECTION("const json + cbegin/cend") { - json::const_iterator it_begin = j_const.cbegin(); + const json::const_iterator it_begin = j_const.cbegin(); json::const_iterator it_end = j_const.cend(); CHECK(it_begin == it_end); } SECTION("json + rbegin/rend") { - json::reverse_iterator it = j.rbegin(); + const json::reverse_iterator it = j.rbegin(); CHECK(it == j.rend()); } SECTION("json + crbegin/crend") { - json::const_reverse_iterator it = j.crbegin(); + const json::const_reverse_iterator it = j.crbegin(); CHECK(it == j.crend()); } SECTION("const json + crbegin/crend") { - json::const_reverse_iterator it = j_const.crbegin(); + const json::const_reverse_iterator it = j_const.crbegin(); CHECK(it == j_const.crend()); } diff --git a/tests/src/unit-iterators2.cpp b/tests/src/unit-iterators2.cpp index ce8c95ba4a..ee9d15c6f2 100644 --- a/tests/src/unit-iterators2.cpp +++ b/tests/src/unit-iterators2.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT // cmake/test.cmake selects the C++ standard versions with which to build a @@ -955,7 +955,8 @@ TEST_CASE("iterators 2") }; json j_expected{"a_key", "b_key", "c_key"}; - auto transformed = j.items() | std::views::transform([](const auto & item) + // NOLINTNEXTLINE(fuchsia-trailing-return) + auto transformed = j.items() | std::views::transform([](const auto & item) -> std::string_view { return item.key(); }); diff --git a/tests/src/unit-iterators3.cpp b/tests/src/unit-iterators3.cpp new file mode 100644 index 0000000000..f9b86ade39 --- /dev/null +++ b/tests/src/unit-iterators3.cpp @@ -0,0 +1,35 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ (supporting code) +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + +#include "doctest_compatibility.h" + +#include +#include +#include + +#include +using nlohmann::json; + +#if (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 +#endif + +#ifdef JSON_HAS_CPP_14 +TEST_CASE_TEMPLATE("checking forward-iterators", T, // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) + std::vector, std::string, nlohmann::json) +{ + auto it1 = typename T::iterator{}; + auto it2 = typename T::iterator{}; + CHECK(it1 == it2); + CHECK(it1 <= it2); + CHECK(it1 >= it2); + CHECK_FALSE(it1 != it2); + CHECK_FALSE(it1 < it2); + CHECK_FALSE(it1 > it2); +} +#endif diff --git a/tests/src/unit-json_patch.cpp b/tests/src/unit-json_patch.cpp index 0999393e6c..fa375b2271 100644 --- a/tests/src/unit-json_patch.cpp +++ b/tests/src/unit-json_patch.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -24,9 +24,9 @@ TEST_CASE("JSON patch") SECTION("4. Operations") { // the ordering of members in JSON objects is not significant: - json op1 = R"({ "op": "add", "path": "/a/b/c", "value": "foo" })"_json; - json op2 = R"({ "path": "/a/b/c", "op": "add", "value": "foo" })"_json; - json op3 = R"({ "value": "foo", "path": "/a/b/c", "op": "add" })"_json; + const json op1 = R"({ "op": "add", "path": "/a/b/c", "value": "foo" })"_json; + const json op2 = R"({ "path": "/a/b/c", "op": "add", "value": "foo" })"_json; + const json op3 = R"({ "value": "foo", "path": "/a/b/c", "op": "add" })"_json; // check if the operation objects are equivalent CHECK(op1 == op2); @@ -60,7 +60,11 @@ TEST_CASE("JSON patch") json const doc2 = R"({ "q": { "bar": 2 } })"_json; // because "a" does not exist. +#if JSON_DIAGNOSTIC_POSITIONS + CHECK_THROWS_WITH_AS(doc2.patch(patch1), "[json.exception.out_of_range.403] (bytes 0-21) key 'a' not found", json::out_of_range&); +#else CHECK_THROWS_WITH_AS(doc2.patch(patch1), "[json.exception.out_of_range.403] key 'a' not found", json::out_of_range&); +#endif json const doc3 = R"({ "a": {} })"_json; json const patch2 = R"([{ "op": "add", "path": "/a/b/c", "value": 1 }])"_json; @@ -68,6 +72,8 @@ TEST_CASE("JSON patch") // should cause an error because "b" does not exist in doc3 #if JSON_DIAGNOSTICS CHECK_THROWS_WITH_AS(doc3.patch(patch2), "[json.exception.out_of_range.403] (/a) key 'b' not found", json::out_of_range&); +#elif JSON_DIAGNOSTIC_POSITIONS + CHECK_THROWS_WITH_AS(doc3.patch(patch2), "[json.exception.out_of_range.403] (bytes 7-9) key 'b' not found", json::out_of_range&); #else CHECK_THROWS_WITH_AS(doc3.patch(patch2), "[json.exception.out_of_range.403] key 'b' not found", json::out_of_range&); #endif @@ -333,6 +339,8 @@ TEST_CASE("JSON patch") CHECK_THROWS_AS(doc.patch(patch), json::other_error&); #if JSON_DIAGNOSTICS CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] (/0) unsuccessful: " + patch[0].dump()); +#elif JSON_DIAGNOSTIC_POSITIONS + CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] (bytes 47-95) unsuccessful: " + patch[0].dump()); #else CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] unsuccessful: " + patch[0].dump()); #endif @@ -417,8 +425,11 @@ TEST_CASE("JSON patch") // applied), because the "add" operation's target location that // references neither the root of the document, nor a member of // an existing object, nor a member of an existing array. - +#if JSON_DIAGNOSTIC_POSITIONS + CHECK_THROWS_WITH_AS(doc.patch(patch), "[json.exception.out_of_range.403] (bytes 21-37) key 'baz' not found", json::out_of_range&); +#else CHECK_THROWS_WITH_AS(doc.patch(patch), "[json.exception.out_of_range.403] key 'baz' not found", json::out_of_range&); +#endif } // A.13. Invalid JSON Patch Document @@ -476,6 +487,8 @@ TEST_CASE("JSON patch") CHECK_THROWS_AS(doc.patch(patch), json::other_error&); #if JSON_DIAGNOSTICS CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] (/0) unsuccessful: " + patch[0].dump()); +#elif JSON_DIAGNOSTIC_POSITIONS + CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] (bytes 47-92) unsuccessful: " + patch[0].dump()); #else CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] unsuccessful: " + patch[0].dump()); #endif @@ -629,12 +642,12 @@ TEST_CASE("JSON patch") )"_json; // apply the patch - json target = source.patch(p1); + const json target = source.patch(p1); // target = { "D": "Berlin", "F": "Paris", "GB": "London" } CHECK(target == R"({ "D": "Berlin", "F": "Paris", "GB": "London" })"_json); // create a diff from two JSONs - json p2 = json::diff(target, source); // NOLINT(readability-suspicious-call-argument) + const json p2 = json::diff(target, source); // NOLINT(readability-suspicious-call-argument) // p2 = [{"op": "delete", "path": "/GB"}] CHECK(p2 == R"([{"op":"remove","path":"/GB"}])"_json); } @@ -653,7 +666,7 @@ TEST_CASE("JSON patch") j["/2/en"_json_pointer] = "ugly"; CHECK(j == R"(["good","bad",{"en":"ugly","it":"cattivo"}])"_json); - json flat = j.flatten(); + const json flat = j.flatten(); CHECK(flat == R"({"/0":"good","/1":"bad","/2/en":"ugly","/2/it":"cattivo"})"_json); } } @@ -1205,6 +1218,8 @@ TEST_CASE("JSON patch") CHECK_THROWS_AS(doc.patch(patch), json::other_error&); #if JSON_DIAGNOSTICS CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] (/0) unsuccessful: " + patch[0].dump()); +#elif JSON_DIAGNOSTIC_POSITIONS + CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] (bytes 47-117) unsuccessful: " + patch[0].dump()); #else CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] unsuccessful: " + patch[0].dump()); #endif @@ -1305,7 +1320,7 @@ TEST_CASE("JSON patch") const auto& doc = test["doc"]; const auto& patch = test["patch"]; - if (test.count("error") == 0) + if (test.count("error") == 0) // NOLINT(readability-container-contains) { // if an expected value is given, use it; use doc otherwise const auto& expected = test.value("expected", doc); diff --git a/tests/src/unit-json_pointer.cpp b/tests/src/unit-json_pointer.cpp index 79c67f99dc..e1522c9f73 100644 --- a/tests/src/unit-json_pointer.cpp +++ b/tests/src/unit-json_pointer.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -203,11 +203,15 @@ TEST_CASE("JSON pointers") // escaped access CHECK(j[json::json_pointer("/a~1b")] == j["a/b"]); CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]); - +#if JSON_DIAGNOSTIC_POSITIONS + // unescaped access + CHECK_THROWS_WITH_AS(j.at(json::json_pointer("/a/b")), + "[json.exception.out_of_range.403] (bytes 13-297) key 'a' not found", json::out_of_range&); +#else // unescaped access CHECK_THROWS_WITH_AS(j.at(json::json_pointer("/a/b")), "[json.exception.out_of_range.403] key 'a' not found", json::out_of_range&); - +#endif // unresolved access const json j_primitive = 1; CHECK_THROWS_WITH_AS(j_primitive["/foo"_json_pointer], @@ -652,7 +656,7 @@ TEST_CASE("JSON pointers") SECTION("equality comparison") { const char* ptr_cpstring = "/foo/bar"; - const char ptr_castring[] = "/foo/bar"; // NOLINT(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) + const char ptr_castring[] = "/foo/bar"; // NOLINT(misc-const-correctness,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) std::string ptr_string{"/foo/bar"}; auto ptr1 = json::json_pointer(ptr_string); auto ptr2 = json::json_pointer(ptr_string); @@ -784,4 +788,18 @@ TEST_CASE("JSON pointers") CHECK_FALSE(ptr_oj != ptr); } } + + // build with C++20 + // JSON_HAS_CPP_20 +#if defined(__cpp_char8_t) + SECTION("Using _json_pointer with char8_t literals #4945") + { + const json j = R"({"a": {"b": {"c": 123}}})"_json; + const auto p1 = "/a/b/c"_json_pointer; + CHECK(j[p1] == 123); + + const auto p2 = u8"/a/b/c"_json_pointer; + CHECK(j[p2] == 123); + } +#endif } diff --git a/tests/src/unit-large_json.cpp b/tests/src/unit-large_json.cpp index 9703a54520..98d16e3369 100644 --- a/tests/src/unit-large_json.cpp +++ b/tests/src/unit-large_json.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit-locale-cpp.cpp b/tests/src/unit-locale-cpp.cpp new file mode 100644 index 0000000000..0019d3b923 --- /dev/null +++ b/tests/src/unit-locale-cpp.cpp @@ -0,0 +1,166 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ (supporting code) +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + +#include "doctest_compatibility.h" + +#define JSON_TESTS_PRIVATE +#include +using nlohmann::json; + +#include + +struct ParserImpl final: public nlohmann::json_sax +{ + bool null() override + { + return true; + } + bool boolean(bool /*val*/) override + { + return true; + } + bool number_integer(json::number_integer_t /*val*/) override + { + return true; + } + bool number_unsigned(json::number_unsigned_t /*val*/) override + { + return true; + } + bool number_float(json::number_float_t /*val*/, const json::string_t& s) override + { + float_string_copy = s; + return true; + } + bool string(json::string_t& /*val*/) override + { + return true; + } + bool binary(json::binary_t& /*val*/) override + { + return true; + } + bool start_object(std::size_t /*val*/) override + { + return true; + } + bool key(json::string_t& /*val*/) override + { + return true; + } + bool end_object() override + { + return true; + } + bool start_array(std::size_t /*val*/) override + { + return true; + } + bool end_array() override + { + return true; + } + bool parse_error(std::size_t /*val*/, const std::string& /*val*/, const nlohmann::detail::exception& /*val*/) override + { + return false; + } + + ~ParserImpl() override; + + ParserImpl() + : float_string_copy("not set") + {} + + ParserImpl(const ParserImpl& other) + : float_string_copy(other.float_string_copy) + {} + + ParserImpl(ParserImpl&& other) noexcept + : float_string_copy(std::move(other.float_string_copy)) + {} + + ParserImpl& operator=(const ParserImpl& other) + { + if (this != &other) + { + float_string_copy = other.float_string_copy; + } + return *this; + } + + ParserImpl& operator=(ParserImpl&& other) noexcept + { + if (this != &other) + { + float_string_copy = std::move(other.float_string_copy); + } + return *this; + } + + json::string_t float_string_copy; +}; + +ParserImpl::~ParserImpl() = default; + +TEST_CASE("locale-dependent test (LC_NUMERIC=C)") +{ + WARN_MESSAGE(std::setlocale(LC_NUMERIC, "C") != nullptr, "could not set locale"); + + SECTION("check if locale is properly set") + { + std::array buffer = {}; + CHECK(std::snprintf(buffer.data(), buffer.size(), "%.2f", 12.34) == 5); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + CHECK(std::string(buffer.data()) == "12.34"); + } + + SECTION("parsing") + { + CHECK(json::parse("12.34").dump() == "12.34"); + } + + SECTION("SAX parsing") + { + ParserImpl sax {}; + json::sax_parse( "12.34", &sax ); + CHECK(sax.float_string_copy == "12.34"); + } +} + +TEST_CASE("locale-dependent test (LC_NUMERIC=de_DE)") +{ + if (std::setlocale(LC_NUMERIC, "de_DE") != nullptr) + { + SECTION("check if locale is properly set") + { + std::array buffer = {}; + CHECK(std::snprintf(buffer.data(), buffer.size(), "%.2f", 12.34) == 5); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + const auto snprintf_result = std::string(buffer.data()); + if (snprintf_result != "12,34") + { + CAPTURE(snprintf_result) + MESSAGE("To test if number parsing is locale-independent, we set the locale to de_DE. However, on this system, the decimal separator doesn't change to `,` potentially due to a known musl issue (https://github.com/nlohmann/json/issues/4767)."); + } + } + + SECTION("parsing") + { + CHECK(json::parse("12.34").dump() == "12.34"); + } + + SECTION("SAX parsing") + { + ParserImpl sax{}; + json::sax_parse("12.34", &sax); + CHECK(sax.float_string_copy == "12.34"); + } + } + else + { + MESSAGE("locale de_DE is not usable"); + } +} diff --git a/tests/src/unit-merge_patch.cpp b/tests/src/unit-merge_patch.cpp index 6a8bcfe2a8..f02a1e9919 100644 --- a/tests/src/unit-merge_patch.cpp +++ b/tests/src/unit-merge_patch.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit-meta.cpp b/tests/src/unit-meta.cpp index a522faabb6..4ce2c3c1f3 100644 --- a/tests/src/unit-meta.cpp +++ b/tests/src/unit-meta.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -18,14 +18,14 @@ TEST_CASE("version information") json j = json::meta(); CHECK(j["name"] == "JSON for Modern C++"); - CHECK(j["copyright"] == "(C) 2013-2023 Niels Lohmann"); + CHECK(j["copyright"] == "(C) 2013-2026 Niels Lohmann"); CHECK(j["url"] == "https://github.com/nlohmann/json"); CHECK(j["version"] == json( { - {"string", "3.11.3"}, + {"string", "3.12.0"}, {"major", 3}, - {"minor", 11}, - {"patch", 3} + {"minor", 12}, + {"patch", 0} })); CHECK(j.find("platform") != j.end()); diff --git a/tests/src/unit-modifiers.cpp b/tests/src/unit-modifiers.cpp index fb686780e9..18185ec00b 100644 --- a/tests/src/unit-modifiers.cpp +++ b/tests/src/unit-modifiers.cpp @@ -1,10 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// Copyright (c) 2013-2022 Niels Lohmann . -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit-msgpack.cpp b/tests/src/unit-msgpack.cpp index 61162afaec..02fb7a2e40 100644 --- a/tests/src/unit-msgpack.cpp +++ b/tests/src/unit-msgpack.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -281,7 +281,7 @@ TEST_CASE("MessagePack") // check individual bytes CHECK(result[0] == 0xcd); - auto const restored = static_cast(static_cast(result[1]) * 256 + static_cast(result[2])); + auto const restored = static_cast((static_cast(result[1]) * 256) + static_cast(result[2])); CHECK(restored == i); // roundtrip @@ -479,11 +479,11 @@ TEST_CASE("MessagePack") std::vector const numbers { -32769, - -65536, - -77777, - -1048576, - -2147483648LL, - }; + -65536, + -77777, + -1048576, + -2147483648LL, + }; for (auto i : numbers) { CAPTURE(i) @@ -671,7 +671,7 @@ TEST_CASE("MessagePack") // check individual bytes CHECK(result[0] == 0xcd); - auto const restored = static_cast(static_cast(result[1]) * 256 + static_cast(result[2])); + auto const restored = static_cast((static_cast(result[1]) * 256) + static_cast(result[2])); CHECK(restored == i); // roundtrip @@ -1508,6 +1508,22 @@ TEST_CASE("MessagePack") CHECK(json::from_msgpack(std::vector({0xc4}), true, false).is_discarded()); } + SECTION("unexpected end inside int with stream") + { + json _; + const std::string data = {static_cast(0xd2u), static_cast(0x12u), static_cast(0x34u), static_cast(0x56u)}; + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::istringstream(data, std::ios::binary)), + "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing MessagePack number: unexpected end of input", json::parse_error&); + } + SECTION("misuse wchar for binary") + { + json _; + // creates 0xd2 after UTF-8 decoding, triggers get_elements in wide_string_input_adapter for code coverage + const std::u32string data = {static_cast(0x0280)}; + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(data), + "[json.exception.parse_error.112] parse error at byte 1: wide string type cannot be interpreted as binary data", json::parse_error&); + } + SECTION("unsupported bytes") { SECTION("concrete examples") @@ -1590,7 +1606,7 @@ TEST_CASE("single MessagePack roundtrip") // parse JSON file std::ifstream f_json(filename); - json j1 = json::parse(f_json); + const json j1 = json::parse(f_json); // parse MessagePack file auto packed = utils::read_binary_file(filename + ".msgpack"); @@ -1604,7 +1620,7 @@ TEST_CASE("single MessagePack roundtrip") { SECTION("std::ostringstream") { - std::basic_ostringstream ss; + std::basic_ostringstream ss; json::to_msgpack(j1, ss); json j3 = json::from_msgpack(ss.str()); CHECK(j1 == j3); @@ -1801,7 +1817,7 @@ TEST_CASE("MessagePack roundtrips" * doctest::skip()) INFO_WITH_TEMP(filename + ": std::vector"); // parse JSON file std::ifstream f_json(filename); - json j1 = json::parse(f_json); + const json j1 = json::parse(f_json); // parse MessagePack file auto packed = utils::read_binary_file(filename + ".msgpack"); @@ -1816,7 +1832,7 @@ TEST_CASE("MessagePack roundtrips" * doctest::skip()) INFO_WITH_TEMP(filename + ": std::ifstream"); // parse JSON file std::ifstream f_json(filename); - json j1 = json::parse(f_json); + const json j1 = json::parse(f_json); // parse MessagePack file std::ifstream f_msgpack(filename + ".msgpack", std::ios::binary); @@ -1831,7 +1847,7 @@ TEST_CASE("MessagePack roundtrips" * doctest::skip()) INFO_WITH_TEMP(filename + ": uint8_t* and size"); // parse JSON file std::ifstream f_json(filename); - json j1 = json::parse(f_json); + const json j1 = json::parse(f_json); // parse MessagePack file auto packed = utils::read_binary_file(filename + ".msgpack"); @@ -1864,3 +1880,82 @@ TEST_CASE("MessagePack roundtrips" * doctest::skip()) } } } + +#ifdef JSON_HAS_CPP_17 +// Test suite for verifying MessagePack handling with std::byte input +TEST_CASE("MessagePack with std::byte") +{ + + SECTION("std::byte compatibility") + { + SECTION("vector roundtrip") + { + json original = + { + {"name", "test"}, + {"value", 42}, + {"array", {1, 2, 3}} + }; + + std::vector temp = json::to_msgpack(original); + // Convert the uint8_t vector to std::byte vector + std::vector msgpack_data(temp.size()); + for (size_t i = 0; i < temp.size(); ++i) + { + msgpack_data[i] = std::byte(temp[i]); + } + // Deserialize from std::byte vector back to JSON + json from_bytes; + CHECK_NOTHROW(from_bytes = json::from_msgpack(msgpack_data)); + + CHECK(from_bytes == original); + } + + SECTION("empty vector") + { + const std::vector empty_data; + CHECK_THROWS_WITH_AS([&]() + { + [[maybe_unused]] auto result = json::from_msgpack(empty_data); + return true; + } + (), + "[json.exception.parse_error.110] parse error at byte 1: syntax error while parsing MessagePack value: unexpected end of input", + json::parse_error&); + } + + SECTION("comparison with workaround") + { + json original = + { + {"string", "hello"}, + {"integer", 42}, + {"float", 3.14}, + {"boolean", true}, + {"null", nullptr}, + {"array", {1, 2, 3}}, + {"object", {{"key", "value"}}} + }; + + std::vector temp = json::to_msgpack(original); + + std::vector msgpack_data(temp.size()); + for (size_t i = 0; i < temp.size(); ++i) + { + msgpack_data[i] = std::byte(temp[i]); + } + // Attempt direct deserialization using std::byte input + const json direct_result = json::from_msgpack(msgpack_data); + + // Test the workaround approach: reinterpret as unsigned char* and use iterator range + const auto* const char_start = reinterpret_cast(msgpack_data.data()); + const auto* const char_end = char_start + msgpack_data.size(); + json workaround_result = json::from_msgpack(char_start, char_end); + + // Verify that the final deserialized JSON matches the original JSON + CHECK(direct_result == workaround_result); + CHECK(direct_result == original); + } + } +} +#endif diff --git a/tests/src/unit-no-mem-leak-on-adl-serialize.cpp b/tests/src/unit-no-mem-leak-on-adl-serialize.cpp index 37b10a9161..469fc2c75f 100644 --- a/tests/src/unit-no-mem-leak-on-adl-serialize.cpp +++ b/tests/src/unit-no-mem-leak-on-adl-serialize.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit-noexcept.cpp b/tests/src/unit-noexcept.cpp index bba230d018..5e330c0a12 100644 --- a/tests/src/unit-noexcept.cpp +++ b/tests/src/unit-noexcept.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -12,13 +12,18 @@ DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") +// skip tests if JSON_DisableEnumSerialization=ON (#4384) +#if defined(JSON_DISABLE_ENUM_SERIALIZATION) && (JSON_DISABLE_ENUM_SERIALIZATION == 1) + #define SKIP_TESTS_FOR_ENUM_SERIALIZATION +#endif + #include using nlohmann::json; namespace { -enum test {}; +enum test {}; // NOLINT(cppcoreguidelines-use-enum-class) struct pod {}; struct pod_bis {}; @@ -36,12 +41,16 @@ static_assert(noexcept(json{}), ""); static_assert(noexcept(nlohmann::to_json(std::declval(), 2)), ""); static_assert(noexcept(nlohmann::to_json(std::declval(), 2.5)), ""); static_assert(noexcept(nlohmann::to_json(std::declval(), true)), ""); -static_assert(noexcept(nlohmann::to_json(std::declval(), test{})), ""); -static_assert(noexcept(nlohmann::to_json(std::declval(), pod{})), ""); +#ifndef SKIP_TESTS_FOR_ENUM_SERIALIZATION +static_assert(noexcept(nlohmann::to_json(std::declval(), test {})), ""); +#endif +static_assert(noexcept(nlohmann::to_json(std::declval(), pod {})), ""); static_assert(!noexcept(nlohmann::to_json(std::declval(), pod_bis{})), ""); static_assert(noexcept(json(2)), ""); -static_assert(noexcept(json(test{})), ""); -static_assert(noexcept(json(pod{})), ""); +#ifndef SKIP_TESTS_FOR_ENUM_SERIALIZATION +static_assert(noexcept(json(test {})), ""); +#endif +static_assert(noexcept(json(pod {})), ""); static_assert(noexcept(std::declval().get()), ""); static_assert(!noexcept(std::declval().get()), ""); static_assert(noexcept(json(pod{})), ""); diff --git a/tests/src/unit-ordered_json.cpp b/tests/src/unit-ordered_json.cpp index d77d904b0c..10bb037bc8 100644 --- a/tests/src/unit-ordered_json.cpp +++ b/tests/src/unit-ordered_json.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit-ordered_map.cpp b/tests/src/unit-ordered_map.cpp index ceafea1e97..76ed4f34f3 100644 --- a/tests/src/unit-ordered_map.cpp +++ b/tests/src/unit-ordered_map.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -36,7 +36,7 @@ TEST_CASE("ordered_map") { std::map m {{"eins", "one"}, {"zwei", "two"}, {"drei", "three"}}; ordered_map om(m.begin(), m.end()); - const auto com = om; + const auto com = om; // NOLINT(performance-unnecessary-copy-initialization) SECTION("with Key&&") { @@ -69,7 +69,7 @@ TEST_CASE("ordered_map") { std::map m {{"eins", "one"}, {"zwei", "two"}, {"drei", "three"}}; ordered_map om(m.begin(), m.end()); - const auto com = om; + const auto com = om; // NOLINT(performance-unnecessary-copy-initialization) SECTION("with Key&&") { diff --git a/tests/src/unit-pointer_access.cpp b/tests/src/unit-pointer_access.cpp index 4503a3b731..c1e3419171 100644 --- a/tests/src/unit-pointer_access.cpp +++ b/tests/src/unit-pointer_access.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -19,7 +19,7 @@ TEST_CASE("pointer access") json value = {{"one", 1}, {"two", 2}}; // check if pointers are returned correctly - test_type* p1 = value.get_ptr(); + const test_type* p1 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p1 == value.get()); @@ -77,7 +77,7 @@ TEST_CASE("pointer access") json value = {1, 2, 3, 4}; // check if pointers are returned correctly - test_type* p1 = value.get_ptr(); + const test_type* p1 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p1 == value.get()); @@ -135,7 +135,7 @@ TEST_CASE("pointer access") json value = "hello"; // check if pointers are returned correctly - test_type* p1 = value.get_ptr(); + const test_type* p1 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p1 == value.get()); @@ -193,7 +193,7 @@ TEST_CASE("pointer access") json value = false; // check if pointers are returned correctly - test_type* p1 = value.get_ptr(); + const test_type* p1 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p1 == value.get()); @@ -251,7 +251,7 @@ TEST_CASE("pointer access") json value = 23; // check if pointers are returned correctly - test_type* p1 = value.get_ptr(); + const test_type* p1 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p1 == value.get()); @@ -309,7 +309,7 @@ TEST_CASE("pointer access") json value = 23u; // check if pointers are returned correctly - test_type* p1 = value.get_ptr(); + const test_type* p1 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p1 == value.get()); @@ -326,7 +326,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); @@ -355,7 +355,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); @@ -367,7 +367,7 @@ TEST_CASE("pointer access") json value = 42.23; // check if pointers are returned correctly - test_type* p1 = value.get_ptr(); + const test_type* p1 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p1 == Approx(value.get())); diff --git a/tests/src/unit-readme.cpp b/tests/src/unit-readme.cpp index dc99073e20..1d1dc72281 100644 --- a/tests/src/unit-readme.cpp +++ b/tests/src/unit-readme.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -136,7 +136,7 @@ TEST_CASE("README" * doctest::skip()) j.push_back(true); // comparison - bool x = (j == R"(["foo", 1, true])"_json); // true + const bool x = (j == R"(["foo", 1, true])"_json); // true CHECK(x == true); // iterate the array @@ -154,7 +154,7 @@ TEST_CASE("README" * doctest::skip()) // getter/setter const auto tmp = j[0].get(); // NOLINT(bugprone-unused-local-non-trivial-variable) j[1] = 42; - bool foo{j.at(2)}; + const bool foo{j.at(2)}; CHECK(foo == true); // other stuff @@ -171,7 +171,7 @@ TEST_CASE("README" * doctest::skip()) // find an entry CHECK(o.find("foo") != o.end()); - if (o.find("foo") != o.end()) + if (o.find("foo") != o.end()) // NOLINT(readability-container-contains) { // there is an entry with key "foo" } @@ -242,21 +242,21 @@ TEST_CASE("README" * doctest::skip()) // Booleans bool const b1 = true; json const jb = b1; - bool b2{jb}; + const bool b2{jb}; CHECK(b2 == true); // numbers int const i = 42; json const jn = i; - double f{jn}; + const double f{jn}; CHECK(f == 42); // etc. std::string const vs = js.get(); // NOLINT(bugprone-unused-local-non-trivial-variable) - bool vb = jb.get(); + const bool vb = jb.get(); CHECK(vb == true); - int vi = jn.get(); + const int vi = jn.get(); CHECK(vi == 42); // etc. diff --git a/tests/src/unit-reference_access.cpp b/tests/src/unit-reference_access.cpp index 3b46fe8335..95fd0cbacf 100644 --- a/tests/src/unit-reference_access.cpp +++ b/tests/src/unit-reference_access.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -215,8 +215,8 @@ TEST_CASE("reference access") "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); CHECK_THROWS_WITH_AS(value.get_ref(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); - //CHECK_THROWS_WITH_AS(value.get_ref(), - // "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); CHECK_NOTHROW(value.get_ref()); CHECK_THROWS_WITH_AS(value.get_ref(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); } diff --git a/tests/src/unit-regression1.cpp b/tests/src/unit-regression1.cpp index f5ef5d80bf..475ef511fd 100644 --- a/tests/src/unit-regression1.cpp +++ b/tests/src/unit-regression1.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -11,6 +11,11 @@ // for some reason including this after the json header leads to linker errors with VS 2017... #include +// skip tests if JSON_DisableEnumSerialization=ON (#4384) +#if defined(JSON_DISABLE_ENUM_SERIALIZATION) && (JSON_DISABLE_ENUM_SERIALIZATION == 1) + #define SKIP_TESTS_FOR_ENUM_SERIALIZATION +#endif + #define JSON_TESTS_PRIVATE #include using nlohmann::json; @@ -117,7 +122,7 @@ TEST_CASE("regression tests 1") SECTION("escape_doublequote") { const auto* s = R"(["\"foo\""])"; - json j = json::parse(s); + const json j = json::parse(s); auto expected = R"(["\"foo\""])"_json; CHECK(j == expected); } @@ -169,14 +174,15 @@ TEST_CASE("regression tests 1") } } +#ifndef SKIP_TESTS_FOR_ENUM_SERIALIZATION SECTION("pull request #71 - handle enum type") { - enum { t = 0, u = 102}; + enum { t = 0, u = 102}; // NOLINT(cppcoreguidelines-use-enum-class) json j = json::array(); j.push_back(t); // maybe this is not the place to test this? - json j2 = u; + const json j2 = u; auto anon_enum_value = j2.get(); CHECK(u == anon_enum_value); @@ -191,6 +197,7 @@ TEST_CASE("regression tests 1") {"game_type", t} })); } +#endif SECTION("issue #76 - dump() / parse() not idempotent") { @@ -240,9 +247,9 @@ TEST_CASE("regression tests 1") auto test = j["Test"].get(); CHECK(test == "Test1"); - int number{j["Number"]}; + const int number{j["Number"]}; CHECK(number == 100); - float foo{j["Foo"]}; + const float foo{j["Foo"]}; CHECK(static_cast(foo) == Approx(42.42)); } @@ -364,7 +371,7 @@ TEST_CASE("regression tests 1") { json o = {{"name", "value"}}; - std::string s1 = o["name"]; + const std::string s1 = o["name"]; CHECK(s1 == "value"); std::string s2; @@ -600,8 +607,8 @@ TEST_CASE("regression tests 1") {"object", {{"key1", 1}, {"key2", 2}}}, }; - int at_integer{j.at("/object/key2"_json_pointer)}; - int val_integer = j.value("/object/key2"_json_pointer, 0); + const int at_integer{j.at("/object/key2"_json_pointer)}; + const int val_integer = j.value("/object/key2"_json_pointer, 0); CHECK(at_integer == val_integer); } @@ -1092,7 +1099,7 @@ TEST_CASE("regression tests 1") #if JSON_USE_IMPLICIT_CONVERSIONS SECTION("issue #464 - VS2017 implicit to std::string conversion fix") { - json v = "test"; + const json v = "test"; std::string test; test = v; CHECK(v == "test"); @@ -1102,9 +1109,9 @@ TEST_CASE("regression tests 1") SECTION("issue #465 - roundtrip error while parsing 1000000000000000010E5") { json const j1 = json::parse("1000000000000000010E5"); - std::string s1 = j1.dump(); + const std::string s1 = j1.dump(); json const j2 = json::parse(s1); - std::string s2 = j2.dump(); + const std::string s2 = j2.dump(); CHECK(s1 == s2); } @@ -1236,7 +1243,7 @@ TEST_CASE("regression tests 1") SECTION("example 1") { // create a map - std::map m1 {{"key", 1}}; + const std::map m1 {{"key", 1}}; // create and print a JSON from the map json const j = m1; @@ -1251,7 +1258,7 @@ TEST_CASE("regression tests 1") SECTION("example 2") { // create a map - std::map m1 {{"key", "val"}}; + const std::map m1 {{"key", "val"}}; // create and print a JSON from the map json const j = m1; @@ -1315,7 +1322,7 @@ TEST_CASE("regression tests 1") auto m1 = j1.get>(); auto m2 = j2.get>(); - int i3{j3}; + const int i3{j3}; CHECK( m1 == ( std::map {{ "first", "one" }} )); CHECK( m2 == ( std::map {{ "second", "two" }} )); @@ -1328,10 +1335,10 @@ TEST_CASE("regression tests 1") { std::ifstream is; is.exceptions( - is.exceptions() - | std::ios_base::failbit - | std::ios_base::badbit - ); // handle different exceptions as 'file not found', 'permission denied' + is.exceptions() + | std::ios_base::failbit + | std::ios_base::badbit + ); // handle different exceptions as 'file not found', 'permission denied' is.open(TEST_DATA_DIRECTORY "/regression/working_file.json"); json _; @@ -1341,10 +1348,10 @@ TEST_CASE("regression tests 1") { std::ifstream is; is.exceptions( - is.exceptions() - | std::ios_base::failbit - | std::ios_base::badbit - ); // handle different exceptions as 'file not found', 'permission denied' + is.exceptions() + | std::ios_base::failbit + | std::ios_base::badbit + ); // handle different exceptions as 'file not found', 'permission denied' is.open(TEST_DATA_DIRECTORY "/json_nlohmann_tests/all_unicode.json.cbor", std::ios_base::in | std::ios_base::binary); @@ -1393,14 +1400,24 @@ TEST_CASE("regression tests 1") auto p1 = R"([{"op": "move", "from": "/one/two/three", "path": "/a/b/c"}])"_json; +#if JSON_DIAGNOSTIC_POSITIONS + CHECK_THROWS_WITH_AS(model.patch(p1), + "[json.exception.out_of_range.403] (bytes 0-158) key 'a' not found", json::out_of_range&); +#else CHECK_THROWS_WITH_AS(model.patch(p1), "[json.exception.out_of_range.403] key 'a' not found", json::out_of_range&); +#endif auto p2 = R"([{"op": "copy", "from": "/one/two/three", "path": "/a/b/c"}])"_json; +#if JSON_DIAGNOSTIC_POSITIONS + CHECK_THROWS_WITH_AS(model.patch(p2), + "[json.exception.out_of_range.403] (bytes 0-158) key 'a' not found", json::out_of_range&); +#else CHECK_THROWS_WITH_AS(model.patch(p2), "[json.exception.out_of_range.403] key 'a' not found", json::out_of_range&); +#endif } SECTION("issue #961 - incorrect parsing of indefinite length CBOR strings") @@ -1414,7 +1431,7 @@ TEST_CASE("regression tests 1") '1', '2', '3', 0xFF }; - json j = json::from_cbor(v_cbor); + const json j = json::from_cbor(v_cbor); CHECK(j == "abcd123"); } @@ -1460,7 +1477,7 @@ TEST_CASE("regression tests 1") }; // parse (with callback) and serialize JSON - json j_filtered = json::parse(text, cb); + const json j_filtered = json::parse(text, cb); CHECK(j_filtered == R"({"Image":{"Animated":false,"Height":600,"IDs":[116,943,234,38793], "Title":"View from 15th Floor","Width":800}})"_json); } @@ -1473,7 +1490,7 @@ TEST_CASE("regression tests 1") SECTION("issue #977 - Assigning between different json types") { foo_json lj = ns::foo{3}; - ns::foo ff(lj); + const ns::foo ff(lj); CHECK(lj.is_object()); CHECK(lj.size() == 1); CHECK(lj["x"] == 3); diff --git a/tests/src/unit-regression2.cpp b/tests/src/unit-regression2.cpp index 0172a45ea2..7431b4e8c5 100644 --- a/tests/src/unit-regression2.cpp +++ b/tests/src/unit-regression2.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT // cmake/test.cmake selects the C++ standard versions with which to build a @@ -36,6 +36,30 @@ using ordered_json = nlohmann::ordered_json; #include #endif +#ifdef JSON_HAS_CPP_17 + #if __has_include() + #include + #elif __has_include() + #include + #endif + + ///////////////////////////////////////////////////////////////////// + // for #4804 + ///////////////////////////////////////////////////////////////////// + using json_4804 = nlohmann::basic_json, // BinaryType + void // CustomBaseClass + >; +#endif + #ifdef JSON_HAS_CPP_20 #if __has_include() #include @@ -71,7 +95,7 @@ enum class for_1647 two }; -// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays): this is a false positive +// NOLINTNEXTLINE(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays): this is a false positive NLOHMANN_JSON_SERIALIZE_ENUM(for_1647, { {for_1647::one, "one"}, @@ -94,14 +118,14 @@ struct Data std::string b{}; // NOLINT(readability-redundant-member-init) }; -void from_json(const json& j, Data& data); +void from_json(const json& j, Data& data); // NOLINT(misc-use-internal-linkage) void from_json(const json& j, Data& data) { j["a"].get_to(data.a); j["b"].get_to(data.b); } -bool operator==(Data const& lhs, Data const& rhs); +bool operator==(Data const& lhs, Data const& rhs); // NOLINT(misc-use-internal-linkage) bool operator==(Data const& lhs, Data const& rhs) { return lhs.a == rhs.a && lhs.b == rhs.b; @@ -162,11 +186,11 @@ struct adl_serializer // for #2824 ///////////////////////////////////////////////////////////////////// -class sax_no_exception : public nlohmann::detail::json_sax_dom_parser +class sax_no_exception : public nlohmann::detail::json_sax_dom_parser { public: explicit sax_no_exception(json& j) - : nlohmann::detail::json_sax_dom_parser(j, false) + : nlohmann::detail::json_sax_dom_parser(j, false) {} static bool parse_error(std::size_t /*position*/, const std::string& /*last_token*/, const json::exception& ex) @@ -221,7 +245,7 @@ class FooBar Foo foo{}; // NOLINT(readability-redundant-member-init) }; -inline void from_json(const nlohmann::json& j, FooBar& fb) +inline void from_json(const nlohmann::json& j, FooBar& fb) // NOLINT(misc-use-internal-linkage) { j.at("value").get_to(fb.foo.value); } @@ -233,7 +257,33 @@ inline void from_json(const nlohmann::json& j, FooBar& fb) struct for_3171_base // NOLINT(cppcoreguidelines-special-member-functions) { for_3171_base(const std::string& /*unused*/ = {}) {} - virtual ~for_3171_base() = default; + virtual ~for_3171_base(); + + for_3171_base(const for_3171_base& other) // NOLINT(hicpp-use-equals-default,modernize-use-equals-default) + : str(other.str) + {} + + for_3171_base& operator=(const for_3171_base& other) + { + if (this != &other) + { + str = other.str; + } + return *this; + } + + for_3171_base(for_3171_base&& other) noexcept + : str(std::move(other.str)) + {} + + for_3171_base& operator=(for_3171_base&& other) noexcept + { + if (this != &other) + { + str = std::move(other.str); + } + return *this; + } virtual void _from_json(const json& j) { @@ -243,13 +293,44 @@ struct for_3171_base // NOLINT(cppcoreguidelines-special-member-functions) std::string str{}; // NOLINT(readability-redundant-member-init) }; +for_3171_base::~for_3171_base() = default; + struct for_3171_derived : public for_3171_base { for_3171_derived() = default; + ~for_3171_derived() override; explicit for_3171_derived(const std::string& /*unused*/) { } + + for_3171_derived(const for_3171_derived& other) // NOLINT(hicpp-use-equals-default,modernize-use-equals-default) + : for_3171_base(other) + {} + + for_3171_derived& operator=(const for_3171_derived& other) + { + if (this != &other) + { + for_3171_base::operator=(other); // Call base class assignment operator + } + return *this; + } + + for_3171_derived(for_3171_derived&& other) noexcept + : for_3171_base(std::move(other)) + {} + + for_3171_derived& operator=(for_3171_derived&& other) noexcept + { + if (this != &other) + { + for_3171_base::operator=(std::move(other)); // Call base class move assignment operator + } + return *this; + } }; -inline void from_json(const json& j, for_3171_base& tb) +for_3171_derived::~for_3171_derived() = default; + +inline void from_json(const json& j, for_3171_base& tb) // NOLINT(misc-use-internal-linkage) { tb._from_json(j); } @@ -264,7 +345,7 @@ struct for_3312 std::string name; }; -inline void from_json(const json& j, for_3312& obj) +inline void from_json(const json& j, for_3312& obj) // NOLINT(misc-use-internal-linkage) { j.at("name").get_to(obj.name); } @@ -282,7 +363,7 @@ struct for_3204_foo struct for_3204_bar { - enum constructed_from_t + enum constructed_from_t // NOLINT(cppcoreguidelines-use-enum-class) { constructed_from_none = 0, constructed_from_foo = 1, @@ -320,6 +401,32 @@ inline for_3333::for_3333(const json& j) : for_3333(j.value("x", 0), j.value("y", 0)) {} +///////////////////////////////////////////////////////////////////// +// for #3810 +///////////////////////////////////////////////////////////////////// + +struct Example_3810 +{ + int bla{}; + + Example_3810() = default; +}; + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Example_3810, bla) // NOLINT(misc-use-internal-linkage) + +///////////////////////////////////////////////////////////////////// +// for #4740 +///////////////////////////////////////////////////////////////////// + +#ifdef JSON_HAS_CPP_17 +struct Example_4740 +{ + std::optional host = std::nullopt; + std::optional port = std::nullopt; + NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Example_4740, host, port) +}; +#endif + TEST_CASE("regression tests 2") { SECTION("issue #1001 - Fix memory leak during parser callback") @@ -458,7 +565,7 @@ TEST_CASE("regression tests 2") {"3", {{"a", "testa_3"}, {"b", "testb_3"}}}, }; - std::map expected + const std::map expected { {"1", {"testa_1", "testb_1"}}, {"2", {"testa_2", "testb_2"}}, @@ -475,7 +582,7 @@ TEST_CASE("regression tests 2") const auto length = 300; json dump_test; - dump_test["1"] = std::string(length, -1); + dump_test["1"] = std::string(length, static_cast(-1)); std::string expected = R"({"1":")"; for (int i = 0; i < length; ++i) @@ -492,7 +599,7 @@ TEST_CASE("regression tests 2") const auto length = 500; json dump_test; - dump_test["1"] = std::string(length, -2); + dump_test["1"] = std::string(length, static_cast(-2)); std::string expected = R"({"1":")"; for (int i = 0; i < length; ++i) @@ -551,7 +658,7 @@ TEST_CASE("regression tests 2") #if !(defined(__INTEL_COMPILER) && __cplusplus >= 202000) { const json j; - NonDefaultFromJsonStruct x(j); + const NonDefaultFromJsonStruct x(j); NonDefaultFromJsonStruct y; CHECK(x == y); } @@ -675,15 +782,17 @@ TEST_CASE("regression tests 2") } #ifdef JSON_HAS_CPP_20 +#ifndef _LIBCPP_VERSION // see https://github.com/nlohmann/json/issues/4490 #if __has_include() SECTION("issue #2546 - parsing containers of std::byte") { - const char DATA[] = R"("Hello, world!")"; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + const char DATA[] = R"("Hello, world!")"; // NOLINT(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) const auto s = std::as_bytes(std::span(DATA)); const json j = json::parse(s); CHECK(j.dump() == "\"Hello, world!\""); } #endif +#endif #endif SECTION("issue #2574 - Deserialization to std::array, std::pair, and std::tuple with non-default constructable types fails") @@ -755,6 +864,15 @@ TEST_CASE("regression tests 2") } } + SECTION("issue #4530 - Serialization of empty tuple") + { + const auto source_tuple = std::tuple<>(); + const nlohmann::json j = source_tuple; + + CHECK(j.get() == source_tuple); + CHECK("[]" == j.dump()); + } + SECTION("issue #2865 - ASAN detects memory leaks") { // the code below is expected to not leak memory @@ -818,7 +936,7 @@ TEST_CASE("regression tests 2") SECTION("issue #2982 - to_{binary format} does not provide a mechanism for specifying a custom allocator for the returned type") { std::vector> my_vector; - json j = {1, 2, 3, 4}; + const json j = {1, 2, 3, 4}; json::to_cbor(j, my_vector); json k = json::from_cbor(my_vector); CHECK(j == k); @@ -917,8 +1035,8 @@ TEST_CASE("regression tests 2") SECTION("issue #3204 - ambiguous regression") { - for_3204_bar bar_from_foo([](for_3204_foo) noexcept {}); // NOLINT(performance-unnecessary-value-param) - for_3204_bar bar_from_json([](json) noexcept {}); // NOLINT(performance-unnecessary-value-param) + const for_3204_bar bar_from_foo([](for_3204_foo) noexcept {}); // NOLINT(performance-unnecessary-value-param) + const for_3204_bar bar_from_json([](json) noexcept {}); // NOLINT(performance-unnecessary-value-param) CHECK(bar_from_foo.constructed_from == for_3204_bar::constructed_from_foo); CHECK(bar_from_json.constructed_from == for_3204_bar::constructed_from_json); @@ -931,11 +1049,143 @@ TEST_CASE("regression tests 2") {"x", 1}, {"y", 2} }; - for_3333 p = j; + const for_3333 p = j; CHECK(p.x == 1); CHECK(p.y == 2); } + + SECTION("issue #3810 - ordered_json doesn't support construction from C array of custom type") + { + Example_3810 states[45]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + + // fix "not used" warning + states[0].bla = 1; + + const auto* const expected = R"([{"bla":1},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0}])"; + + // This works: + nlohmann::json j; + j["test"] = states; + CHECK(j["test"].dump() == expected); + + // This doesn't compile: + nlohmann::ordered_json oj; + oj["test"] = states; + CHECK(oj["test"].dump() == expected); + } + +#ifdef JSON_HAS_CPP_17 + SECTION("issue #4740 - build issue with std::optional") + { + const auto t1 = Example_4740(); + const auto j1 = nlohmann::json(t1); + CHECK(j1.dump() == "{\"host\":null,\"port\":null}"); + const auto t2 = j1.get(); + CHECK(!t2.host.has_value()); + CHECK(!t2.port.has_value()); + + // improve coverage + auto t3 = Example_4740(); + t3.port = 80; + t3.host = "example.com"; + const auto j2 = nlohmann::json(t3); + CHECK(j2.dump() == "{\"host\":\"example.com\",\"port\":80}"); + const auto t4 = j2.get(); + CHECK(t4.host.has_value()); + CHECK(t4.port.has_value()); + } +#endif + +#if !defined(_MSVC_LANG) + // MSVC returns garbage on invalid enum values, so this test is excluded + // there. + SECTION("issue #4762 - json exception 302 with unhelpful explanation : type must be number, but is number") + { + // In #4762, the main issue was that a json object with an invalid type + // returned "number" as type_name(), because this was the default case. + // This test makes sure we now return "invalid" instead. + json j; + j.m_data.m_type = static_cast(100); // NOLINT(clang-analyzer-optin.core.EnumCastOutOfRange) + CHECK(j.type_name() == "invalid"); + } +#endif + +#ifdef JSON_HAS_CPP_17 + SECTION("issue #4804: from_cbor incompatible with std::vector as binary_t") + { + const std::vector data = {0x80}; + const auto decoded = json_4804::from_cbor(data); + CHECK((decoded == json_4804::array())); + } +#endif +} + +TEST_CASE_TEMPLATE("issue #4798 - nlohmann::json::to_msgpack() encode float NaN as double", T, double, float) // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) +{ + // With issue #4798, we encode NaN, infinity, and -infinity as float instead + // of double to allow for smaller encodings. + const json jx = std::numeric_limits::quiet_NaN(); + const json jy = std::numeric_limits::infinity(); + const json jz = -std::numeric_limits::infinity(); + + ///////////////////////////////////////////////////////////////////////// + // MessagePack + ///////////////////////////////////////////////////////////////////////// + + // expected MessagePack values + const std::vector msgpack_x = {{0xCA, 0x7F, 0xC0, 0x00, 0x00}}; + const std::vector msgpack_y = {{0xCA, 0x7F, 0x80, 0x00, 0x00}}; + const std::vector msgpack_z = {{0xCA, 0xFF, 0x80, 0x00, 0x00}}; + + CHECK(json::to_msgpack(jx) == msgpack_x); + CHECK(json::to_msgpack(jy) == msgpack_y); + CHECK(json::to_msgpack(jz) == msgpack_z); + + CHECK(std::isnan(json::from_msgpack(msgpack_x).get())); + CHECK(json::from_msgpack(msgpack_y).get() == std::numeric_limits::infinity()); + CHECK(json::from_msgpack(msgpack_z).get() == -std::numeric_limits::infinity()); + + // Make sure the other MessagePakc encodings for NaN, infinity, and + // -infinity are still supported. + const std::vector msgpack_x_2 = {{0xCB, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + const std::vector msgpack_y_2 = {{0xCB, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + const std::vector msgpack_z_2 = {{0xCB, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CHECK(std::isnan(json::from_msgpack(msgpack_x_2).get())); + CHECK(json::from_msgpack(msgpack_y_2).get() == std::numeric_limits::infinity()); + CHECK(json::from_msgpack(msgpack_z_2).get() == -std::numeric_limits::infinity()); + + ///////////////////////////////////////////////////////////////////////// + // CBOR + ///////////////////////////////////////////////////////////////////////// + + // expected CBOR values + const std::vector cbor_x = {{0xF9, 0x7E, 0x00}}; + const std::vector cbor_y = {{0xF9, 0x7C, 0x00}}; + const std::vector cbor_z = {{0xF9, 0xfC, 0x00}}; + + CHECK(json::to_cbor(jx) == cbor_x); + CHECK(json::to_cbor(jy) == cbor_y); + CHECK(json::to_cbor(jz) == cbor_z); + + CHECK(std::isnan(json::from_cbor(cbor_x).get())); + CHECK(json::from_cbor(cbor_y).get() == std::numeric_limits::infinity()); + CHECK(json::from_cbor(cbor_z).get() == -std::numeric_limits::infinity()); + + // Make sure the other CBOR encodings for NaN, infinity, and -infinity are + // still supported. + const std::vector cbor_x_2 = {{0xFA, 0x7F, 0xC0, 0x00, 0x00}}; + const std::vector cbor_y_2 = {{0xFA, 0x7F, 0x80, 0x00, 0x00}}; + const std::vector cbor_z_2 = {{0xFA, 0xFF, 0x80, 0x00, 0x00}}; + const std::vector cbor_x_3 = {{0xFB, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + const std::vector cbor_y_3 = {{0xFB, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + const std::vector cbor_z_3 = {{0xFB, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CHECK(std::isnan(json::from_cbor(cbor_x_2).get())); + CHECK(json::from_cbor(cbor_y_2).get() == std::numeric_limits::infinity()); + CHECK(json::from_cbor(cbor_z_2).get() == -std::numeric_limits::infinity()); + CHECK(std::isnan(json::from_cbor(cbor_x_3).get())); + CHECK(json::from_cbor(cbor_y_3).get() == std::numeric_limits::infinity()); + CHECK(json::from_cbor(cbor_z_3).get() == -std::numeric_limits::infinity()); } DOCTEST_CLANG_SUPPRESS_WARNING_POP diff --git a/tests/src/unit-serialization.cpp b/tests/src/unit-serialization.cpp index e8f0e7e5bf..5c2ab81482 100644 --- a/tests/src/unit-serialization.cpp +++ b/tests/src/unit-serialization.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -165,7 +165,7 @@ TEST_CASE("serialization") } } -TEST_CASE_TEMPLATE("serialization for extreme integer values", T, int32_t, uint32_t, int64_t, uint64_t) +TEST_CASE_TEMPLATE("serialization for extreme integer values", T, int32_t, uint32_t, int64_t, uint64_t) // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) { SECTION("minimum") { diff --git a/tests/src/unit-testsuites.cpp b/tests/src/unit-testsuites.cpp index 5807934baa..6729819675 100644 --- a/tests/src/unit-testsuites.cpp +++ b/tests/src/unit-testsuites.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -163,7 +163,7 @@ TEST_CASE("compliance tests from nativejson-benchmark") TEST_DOUBLE("[2.2250738585072012e-308]", 2.2250738585072014e-308); - // More closer to normal/subnormal boundary + // Closer to normal/subnormal boundary // boundary = 2^-1022 - 2^-1075 = 2.225073858507201136057409796709131975934819546351645648... ¡Á 10^-308 TEST_DOUBLE("[2.22507385850720113605740979670913197593481954635164564e-308]", 2.2250738585072009e-308); @@ -312,7 +312,7 @@ TEST_CASE("test suite from json-test-suite") { SECTION("read all sample.json") { - // read a file with all unicode characters stored as single-character + // read a file with all Unicode characters stored as single-character // strings in a JSON array std::ifstream f(TEST_DATA_DIRECTORY "/json_testsuite/sample.json"); json j; @@ -326,6 +326,7 @@ TEST_CASE("test suite from json-test-suite") TEST_CASE("json.org examples") { // here, we list all JSON values from https://json.org/example + using FilePtr = std::unique_ptr; SECTION("1.json") { @@ -363,35 +364,35 @@ TEST_CASE("json.org examples") } SECTION("FILE 1.json") { - const std::unique_ptr f(std::fopen(TEST_DATA_DIRECTORY "/json.org/1.json", "r"), &std::fclose); + const FilePtr f(std::fopen(TEST_DATA_DIRECTORY "/json.org/1.json", "r"), &std::fclose); json _; CHECK_NOTHROW(_ = json::parse(f.get())); } SECTION("FILE 2.json") { - const std::unique_ptr f(std::fopen(TEST_DATA_DIRECTORY "/json.org/2.json", "r"), &std::fclose); + const FilePtr f(std::fopen(TEST_DATA_DIRECTORY "/json.org/2.json", "r"), &std::fclose); json _; CHECK_NOTHROW(_ = json::parse(f.get())); } SECTION("FILE 3.json") { - const std::unique_ptr f(std::fopen(TEST_DATA_DIRECTORY "/json.org/3.json", "r"), &std::fclose); + const FilePtr f(std::fopen(TEST_DATA_DIRECTORY "/json.org/3.json", "r"), &std::fclose); json _; CHECK_NOTHROW(_ = json::parse(f.get())); } SECTION("FILE 4.json") { - const std::unique_ptr f(std::fopen(TEST_DATA_DIRECTORY "/json.org/4.json", "r"), &std::fclose); + const FilePtr f(std::fopen(TEST_DATA_DIRECTORY "/json.org/4.json", "r"), &std::fclose); json _; CHECK_NOTHROW(_ = json::parse(f.get())); } SECTION("FILE 5.json") { - const std::unique_ptr f(std::fopen(TEST_DATA_DIRECTORY "/json.org/5.json", "r"), &std::fclose); + const FilePtr f(std::fopen(TEST_DATA_DIRECTORY "/json.org/5.json", "r"), &std::fclose); json _; CHECK_NOTHROW(_ = json::parse(f.get())); } diff --git a/tests/src/unit-to_chars.cpp b/tests/src/unit-to_chars.cpp index 6d32e06566..def2793c2a 100644 --- a/tests/src/unit-to_chars.cpp +++ b/tests/src/unit-to_chars.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT // XXX: @@ -340,7 +340,7 @@ TEST_CASE("formatting") { std::array buf{}; char* end = nlohmann::detail::to_chars(buf.data(), buf.data() + 32, number); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) - std::string actual(buf.data(), end); + const std::string actual(buf.data(), end); CHECK(actual == expected); }; @@ -400,7 +400,7 @@ TEST_CASE("formatting") { std::array buf{}; char* end = nlohmann::detail::to_chars(buf.data(), buf.data() + 32, number); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) - std::string actual(buf.data(), end); + const std::string actual(buf.data(), end); CHECK(actual == expected); }; diff --git a/tests/src/unit-type_traits.cpp b/tests/src/unit-type_traits.cpp index 4c78aa9900..d083293dd4 100644 --- a/tests/src/unit-type_traits.cpp +++ b/tests/src/unit-type_traits.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit-ubjson.cpp b/tests/src/unit-ubjson.cpp index 06611c5fe1..99d07d9669 100644 --- a/tests/src/unit-ubjson.cpp +++ b/tests/src/unit-ubjson.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -450,7 +450,7 @@ TEST_CASE("UBJSON") // check individual bytes CHECK(result[0] == 'I'); - auto const restored = static_cast(static_cast(result[1]) * 256 + static_cast(result[2])); + auto const restored = static_cast((static_cast(result[1]) * 256) + static_cast(result[2])); CHECK(restored == i); // roundtrip @@ -655,7 +655,7 @@ TEST_CASE("UBJSON") // check individual bytes CHECK(result[0] == 'I'); - auto const restored = static_cast(static_cast(result[1]) * 256 + static_cast(result[2])); + auto const restored = static_cast((static_cast(result[1]) * 256) + static_cast(result[2])); CHECK(restored == i); // roundtrip @@ -1617,7 +1617,7 @@ TEST_CASE("UBJSON") CHECK_THROWS_AS(_ = json::from_ubjson(v_ubjson), json::out_of_range&); json j; - nlohmann::detail::json_sax_dom_callback_parser scp(j, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) noexcept + nlohmann::detail::json_sax_dom_callback_parser scp(j, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) noexcept { return true; }); @@ -1631,7 +1631,7 @@ TEST_CASE("UBJSON") CHECK_THROWS_AS(_ = json::from_ubjson(v_ubjson), json::out_of_range&); json j; - nlohmann::detail::json_sax_dom_callback_parser scp(j, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) noexcept + nlohmann::detail::json_sax_dom_callback_parser scp(j, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) noexcept { return true; }); @@ -2515,7 +2515,7 @@ TEST_CASE("UBJSON roundtrips" * doctest::skip()) INFO_WITH_TEMP(filename + ": uint8_t* and size"); // parse JSON file std::ifstream f_json(filename); - json j1 = json::parse(f_json); + const json j1 = json::parse(f_json); // parse UBJSON file auto const packed = utils::read_binary_file(filename + ".ubjson"); diff --git a/tests/src/unit-udl.cpp b/tests/src/unit-udl.cpp index bb72ccbceb..405f4792d5 100644 --- a/tests/src/unit-udl.cpp +++ b/tests/src/unit-udl.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit-udt.cpp b/tests/src/unit-udt.cpp index 57cf278d88..24f763f96e 100644 --- a/tests/src/unit-udt.cpp +++ b/tests/src/unit-udt.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -12,6 +12,11 @@ DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") +// skip tests if JSON_DisableEnumSerialization=ON (#4384) +#if defined(JSON_DISABLE_ENUM_SERIALIZATION) && (JSON_DISABLE_ENUM_SERIALIZATION == 1) + #define SKIP_TESTS_FOR_ENUM_SERIALIZATION +#endif + #include using nlohmann::json; #ifdef JSON_TEST_NO_GLOBAL_UDLS @@ -132,7 +137,11 @@ static void to_json(nlohmann::json& j, const contact& c) static void to_json(nlohmann::json& j, const contact_book& cb) { - j = json{{"name", cb.m_book_name}, {"id", cb.m_book_id}, {"contacts", cb.m_contacts}}; + j = json{{"name", cb.m_book_name}, +#ifndef SKIP_TESTS_FOR_ENUM_SERIALIZATION + {"id", cb.m_book_id}, +#endif + {"contacts", cb.m_contacts}}; } // operators @@ -222,7 +231,9 @@ static void from_json(const nlohmann::json& j, contact& c) static void from_json(const nlohmann::json& j, contact_book& cb) { cb.m_book_name = j["name"].get(); +#ifndef SKIP_TESTS_FOR_ENUM_SERIALIZATION cb.m_book_id = j["id"].get(); +#endif cb.m_contacts = j["contacts"].get>(); } } // namespace udt @@ -253,14 +264,22 @@ TEST_CASE("basic usage" * doctest::test_suite("udt")) CHECK(json("Paris") == json(addr)); CHECK(json(cpp_programmer) == R"({"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"})"_json); +#ifndef SKIP_TESTS_FOR_ENUM_SERIALIZATION CHECK(json(large_id) == json(static_cast(1) << 63)); CHECK(json(large_id) > 0u); CHECK(to_string(json(large_id)) == "9223372036854775808"); CHECK(json(large_id).is_number_unsigned()); +#endif +#ifndef SKIP_TESTS_FOR_ENUM_SERIALIZATION CHECK( json(book) == R"({"name":"C++", "id":42, "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"中华人民共和国", "name":"王芳"}, "address":"Paris"}]})"_json); +#else + CHECK( + json(book) == + R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"中华人民共和国", "name":"王芳"}, "address":"Paris"}]})"_json); +#endif } @@ -272,7 +291,9 @@ TEST_CASE("basic usage" * doctest::test_suite("udt")) { const auto parsed_book = big_json.get(); const auto book_name = big_json["name"].get(); +#ifndef SKIP_TESTS_FOR_ENUM_SERIALIZATION const auto book_id = big_json["id"].get(); +#endif const auto contacts = big_json["contacts"].get>(); const auto contact_json = big_json["contacts"].at(0); @@ -292,8 +313,10 @@ TEST_CASE("basic usage" * doctest::test_suite("udt")) CHECK(contact == cpp_programmer); CHECK(contacts == book.m_contacts); CHECK(book_name == udt::name{"C++"}); +#ifndef SKIP_TESTS_FOR_ENUM_SERIALIZATION CHECK(book_id == book.m_book_id); CHECK(book == parsed_book); +#endif } SECTION("via explicit calls to get_to") @@ -314,7 +337,9 @@ TEST_CASE("basic usage" * doctest::test_suite("udt")) { const udt::contact_book parsed_book = big_json; const udt::name book_name = big_json["name"]; +#ifndef SKIP_TESTS_FOR_ENUM_SERIALIZATION const udt::book_id book_id = big_json["id"]; +#endif const std::vector contacts = big_json["contacts"]; const auto contact_json = big_json["contacts"].at(0); const udt::contact contact = contact_json; @@ -332,8 +357,10 @@ TEST_CASE("basic usage" * doctest::test_suite("udt")) CHECK(contact == cpp_programmer); CHECK(contacts == book.m_contacts); CHECK(book_name == udt::name{"C++"}); +#ifndef SKIP_TESTS_FOR_ENUM_SERIALIZATION CHECK(book_id == static_cast(42u)); CHECK(book == parsed_book); +#endif } #endif } @@ -708,14 +735,14 @@ TEST_CASE("different basic_json types conversions") SECTION("null") { json const j; - custom_json cj = j; + const custom_json cj = j; CHECK(cj == nullptr); } SECTION("boolean") { json const j = true; - custom_json cj = j; + const custom_json cj = j; CHECK(cj == true); } @@ -737,28 +764,28 @@ TEST_CASE("different basic_json types conversions") SECTION("integer") { json const j = 42; - custom_json cj = j; + const custom_json cj = j; CHECK(cj == 42); } SECTION("float") { json const j = 42.0; - custom_json cj = j; + const custom_json cj = j; CHECK(cj == 42.0); } SECTION("unsigned") { json const j = 42u; - custom_json cj = j; + const custom_json cj = j; CHECK(cj == 42u); } SECTION("string") { json const j = "forty-two"; - custom_json cj = j; + const custom_json cj = j; CHECK(cj == "forty-two"); } @@ -767,7 +794,7 @@ TEST_CASE("different basic_json types conversions") json j = json::binary({1, 2, 3}, 42); custom_json cj = j; CHECK(cj.get_binary().subtype() == 42); - std::vector cv = cj.get_binary(); + const std::vector& cv = cj.get_binary(); std::vector v = j.get_binary(); CHECK(cv == v); } @@ -775,7 +802,7 @@ TEST_CASE("different basic_json types conversions") SECTION("object") { json const j = {{"forty", "two"}}; - custom_json cj = j; + const custom_json cj = j; auto m = j.get>(); CHECK(cj == m); } @@ -783,7 +810,7 @@ TEST_CASE("different basic_json types conversions") SECTION("get") { json const j = 42; - custom_json cj = j.get(); + const custom_json cj = j.get(); CHECK(cj == 42); } } @@ -814,7 +841,7 @@ class Evil public: Evil() = default; template - Evil(T t) : m_i(sizeof(t)) + Evil(const T& t) : m_i(sizeof(t)) { static_cast(t); // fix MSVC's C4100 warning } @@ -834,8 +861,12 @@ TEST_CASE("Issue #924") CHECK_NOTHROW(j.get>()); // silence Wunused-template warnings - Evil e(1); + const Evil e(1); CHECK(e.m_i >= 0); + + // suppress warning: function "::Evil::Evil(T) [with T=std::string]" was declared but never referenced [declared_but_not_referenced] + const Evil e2(std::string("foo")); + CHECK(e2.m_i >= 0); } TEST_CASE("Issue #1237") diff --git a/tests/src/unit-udt_macro.cpp b/tests/src/unit-udt_macro.cpp index 04631a4dc0..cd65134297 100644 --- a/tests/src/unit-udt_macro.cpp +++ b/tests/src/unit-udt_macro.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include @@ -38,6 +38,26 @@ class person_with_private_data NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_with_private_data, age, name, metadata) }; +class derived_person_with_private_data : public person_with_private_data +{ + private: + std::string hair_color{"blue"}; + + public: + bool operator==(const derived_person_with_private_data& rhs) const + { + return person_with_private_data::operator==(rhs) && hair_color == rhs.hair_color; + } + + derived_person_with_private_data() = default; + derived_person_with_private_data(std::string name_, int age_, json metadata_, std::string hair_color_) + : person_with_private_data(std::move(name_), age_, std::move(metadata_)) + , hair_color(std::move(hair_color_)) + {} + + NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(derived_person_with_private_data, person_with_private_data, hair_color) +}; + class person_with_private_data_2 { private: @@ -74,6 +94,31 @@ class person_with_private_data_2 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(person_with_private_data_2, age, name, metadata) }; +class derived_person_with_private_data_2 : public person_with_private_data_2 +{ + private: + std::string hair_color{"blue"}; + + public: + bool operator==(const derived_person_with_private_data_2& rhs) const + { + return person_with_private_data_2::operator==(rhs) && hair_color == rhs.hair_color; + } + + derived_person_with_private_data_2() = default; + derived_person_with_private_data_2(std::string name_, int age_, json metadata_, std::string hair_color_) + : person_with_private_data_2(std::move(name_), age_, std::move(metadata_)) + , hair_color(std::move(hair_color_)) + {} + + std::string getHairColor() const + { + return hair_color; + } + + NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT(derived_person_with_private_data_2, person_with_private_data_2, hair_color) +}; + class person_without_private_data_1 { public: @@ -96,6 +141,26 @@ class person_without_private_data_1 NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_without_private_data_1, age, name, metadata) }; +class derived_person_without_private_data_1 : public person_without_private_data_1 +{ + public: + std::string hair_color{"blue"}; + + public: + bool operator==(const derived_person_without_private_data_1& rhs) const + { + return person_without_private_data_1::operator==(rhs) && hair_color == rhs.hair_color; + } + + derived_person_without_private_data_1() = default; + derived_person_without_private_data_1(std::string name_, int age_, json metadata_, std::string hair_color_) + : person_without_private_data_1(std::move(name_), age_, std::move(metadata_)) + , hair_color(std::move(hair_color_)) + {} + + NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(derived_person_without_private_data_1, person_without_private_data_1, hair_color) +}; + class person_without_private_data_2 { public: @@ -116,8 +181,30 @@ class person_without_private_data_2 {} }; +// NOLINTNEXTLINE(misc-use-internal-linkage) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_without_private_data_2, age, name, metadata) +class derived_person_without_private_data_2 : public person_without_private_data_2 +{ + public: + std::string hair_color{"blue"}; + + public: + bool operator==(const derived_person_without_private_data_2& rhs) const + { + return person_without_private_data_2::operator==(rhs) && hair_color == rhs.hair_color; + } + + derived_person_without_private_data_2() = default; + derived_person_without_private_data_2(std::string name_, int age_, json metadata_, std::string hair_color_) + : person_without_private_data_2(std::move(name_), age_, std::move(metadata_)) + , hair_color(std::move(hair_color_)) + {} +}; + +// NOLINTNEXTLINE(misc-use-internal-linkage) +NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(derived_person_without_private_data_2, person_without_private_data_2, hair_color) + class person_without_private_data_3 { public: @@ -151,8 +238,35 @@ class person_without_private_data_3 } }; +// NOLINTNEXTLINE(misc-use-internal-linkage) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(person_without_private_data_3, age, name, metadata) +class derived_person_without_private_data_3 : public person_without_private_data_3 +{ + public: + std::string hair_color{"blue"}; + + public: + bool operator==(const derived_person_without_private_data_3& rhs) const + { + return person_without_private_data_3::operator==(rhs) && hair_color == rhs.hair_color; + } + + derived_person_without_private_data_3() = default; + derived_person_without_private_data_3(std::string name_, int age_, json metadata_, std::string hair_color_) + : person_without_private_data_3(std::move(name_), age_, std::move(metadata_)) + , hair_color(std::move(hair_color_)) + {} + + std::string getHairColor() const + { + return hair_color; + } +}; + +// NOLINTNEXTLINE(misc-use-internal-linkage) +NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT(derived_person_without_private_data_3, person_without_private_data_3, hair_color) + class person_with_private_alphabet { public: @@ -277,6 +391,7 @@ class person_with_public_alphabet int z = 0; }; +// NOLINTNEXTLINE(misc-use-internal-linkage) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_with_public_alphabet, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z) class person_without_default_constructor_1 @@ -315,160 +430,280 @@ class person_without_default_constructor_2 {} }; +// NOLINTNEXTLINE(misc-use-internal-linkage) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(person_without_default_constructor_2, name, age) +class derived_person_only_serialize_public : public person_without_default_constructor_1 +{ + public: + std::string hair_color; + + derived_person_only_serialize_public(std::string name_, int age_, std::string hair_color_) + : person_without_default_constructor_1(std::move(name_), age_) + , hair_color(std::move(hair_color_)) + {} +}; + +// NOLINTNEXTLINE(misc-use-internal-linkage) +NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(derived_person_only_serialize_public, person_without_default_constructor_1, hair_color) + +class derived_person_only_serialize_private : person_without_default_constructor_1 +{ + private: + std::string hair_color; + public: + derived_person_only_serialize_private(std::string name_, int age_, std::string hair_color_) + : person_without_default_constructor_1(std::move(name_), age_) + , hair_color(std::move(hair_color_)) + {} + + NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE(derived_person_only_serialize_private, person_without_default_constructor_1, hair_color) +}; + } // namespace persons -TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE", T, - persons::person_with_private_data, - persons::person_without_private_data_1, - persons::person_without_private_data_2) +TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE", Pair, // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair) { + using Json = typename Pair::first_type; + using T = typename Pair::second_type; + constexpr bool is_ordered = std::is_same::value; + SECTION("person") { // serialization T p1("Erik", 1, {{"haircuts", 2}}); - CHECK(json(p1).dump() == "{\"age\":1,\"metadata\":{\"haircuts\":2},\"name\":\"Erik\"}"); + CHECK(Json(p1).dump() == (is_ordered ? + R"({"age":1,"name":"Erik","metadata":{"haircuts":2}})" : + R"({"age":1,"metadata":{"haircuts":2},"name":"Erik"})")); + + // deserialization + auto p2 = Json(p1).template get(); + CHECK(p2 == p1); + + // roundtrip + CHECK(T(Json(p1)) == p1); + CHECK(Json(T(Json(p1))) == Json(p1)); + + // check exception in case of missing field + Json j = Json(p1); + j.erase("age"); + CHECK_THROWS_WITH_AS(j.template get(), "[json.exception.out_of_range.403] key 'age' not found", typename Json::out_of_range); + } +} + +TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE and NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE", Pair, // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair) +{ + using Json = typename Pair::first_type; + using T = typename Pair::second_type; + constexpr bool is_ordered = std::is_same::value; + + SECTION("person") + { + // serialization + T p1("Erik", 1, {{"haircuts", 2}}, "red"); + CHECK(Json(p1).dump() == (is_ordered ? + R"({"age":1,"name":"Erik","metadata":{"haircuts":2},"hair_color":"red"})" : + R"({"age":1,"hair_color":"red","metadata":{"haircuts":2},"name":"Erik"})")); // deserialization - auto p2 = json(p1).get(); + auto p2 = Json(p1).template get(); CHECK(p2 == p1); // roundtrip - CHECK(T(json(p1)) == p1); - CHECK(json(T(json(p1))) == json(p1)); + CHECK(T(Json(p1)) == p1); + CHECK(Json(T(Json(p1))) == Json(p1)); // check exception in case of missing field - json j = json(p1); + Json j = Json(p1); j.erase("age"); - CHECK_THROWS_WITH_AS(j.get(), "[json.exception.out_of_range.403] key 'age' not found", json::out_of_range); + CHECK_THROWS_WITH_AS(j.template get(), "[json.exception.out_of_range.403] key 'age' not found", typename Json::out_of_range); } } -TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT", T, - persons::person_with_private_data_2, - persons::person_without_private_data_3) +TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT", Pair, // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) + std::pair, + std::pair, + std::pair, + std::pair) { + using Json = typename Pair::first_type; + using T = typename Pair::second_type; + constexpr bool is_ordered = std::is_same::value; + SECTION("person with default values") { // serialization of default constructed object - T p0; - CHECK(json(p0).dump() == "{\"age\":0,\"metadata\":null,\"name\":\"\"}"); + const T p0{}; + CHECK(Json(p0).dump() == (is_ordered ? + R"({"age":0,"name":"","metadata":null})" : + R"({"age":0,"metadata":null,"name":""})")); // serialization T p1("Erik", 1, {{"haircuts", 2}}); - CHECK(json(p1).dump() == "{\"age\":1,\"metadata\":{\"haircuts\":2},\"name\":\"Erik\"}"); + CHECK(Json(p1).dump() == (is_ordered ? + R"({"age":1,"name":"Erik","metadata":{"haircuts":2}})" : + R"({"age":1,"metadata":{"haircuts":2},"name":"Erik"})")); // deserialization - auto p2 = json(p1).get(); + auto p2 = Json(p1).template get(); CHECK(p2 == p1); // roundtrip - CHECK(T(json(p1)) == p1); - CHECK(json(T(json(p1))) == json(p1)); + CHECK(T(Json(p1)) == p1); + CHECK(Json(T(Json(p1))) == Json(p1)); // check default value in case of missing field - json j = json(p1); + Json j = Json(p1); j.erase("name"); j.erase("age"); j.erase("metadata"); - T p3 = j.get(); + const T p3 = j.template get(); CHECK(p3.getName() == ""); CHECK(p3.getAge() == 0); CHECK(p3.getMetadata() == nullptr); + + // check default value in case of empty json + const Json j4; + const T p4 = j4.template get(); + CHECK(p4.getName() == ""); + CHECK(p4.getAge() == 0); + CHECK(p4.getMetadata() == nullptr); } } -TEST_CASE_TEMPLATE("Serialization/deserialization of classes with 26 public/private member variables via NLOHMANN_DEFINE_TYPE_INTRUSIVE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE", T, - persons::person_with_private_alphabet, - persons::person_with_public_alphabet) +TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT and NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT", Pair, // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) + std::pair, + std::pair, + std::pair, + std::pair) { - SECTION("alphabet") + using Json = typename Pair::first_type; + using T = typename Pair::second_type; + constexpr bool is_ordered = std::is_same::value; + + SECTION("derived person with default values") { - { - T obj1; - nlohmann::json const j = obj1; //via json object - T obj2; - j.get_to(obj2); - bool ok = (obj1 == obj2); - CHECK(ok); - } + // serialization of default constructed object + const T p0{}; + CHECK(Json(p0).dump() == (is_ordered ? + R"({"age":0,"name":"","metadata":null,"hair_color":"blue"})" : + R"({"age":0,"hair_color":"blue","metadata":null,"name":""})")); - { - T obj1; - nlohmann::json const j1 = obj1; //via json string - std::string const s = j1.dump(); - nlohmann::json const j2 = nlohmann::json::parse(s); - T obj2; - j2.get_to(obj2); - bool ok = (obj1 == obj2); - CHECK(ok); - } + // serialization + T p1("Erik", 1, {{"haircuts", 2}}, "red"); + CHECK(Json(p1).dump() == (is_ordered ? + R"({"age":1,"name":"Erik","metadata":{"haircuts":2},"hair_color":"red"})" : + R"({"age":1,"hair_color":"red","metadata":{"haircuts":2},"name":"Erik"})")); - { - T obj1; - nlohmann::json const j1 = obj1; //via msgpack - std::vector const buf = nlohmann::json::to_msgpack(j1); - nlohmann::json const j2 = nlohmann::json::from_msgpack(buf); - T obj2; - j2.get_to(obj2); - bool ok = (obj1 == obj2); - CHECK(ok); - } + // deserialization + auto p2 = Json(p1).template get(); + CHECK(p2 == p1); - { - T obj1; - nlohmann::json const j1 = obj1; //via bson - std::vector const buf = nlohmann::json::to_bson(j1); - nlohmann::json const j2 = nlohmann::json::from_bson(buf); - T obj2; - j2.get_to(obj2); - bool ok = (obj1 == obj2); - CHECK(ok); - } + // roundtrip + CHECK(T(Json(p1)) == p1); + CHECK(Json(T(Json(p1))) == Json(p1)); - { - T obj1; - nlohmann::json const j1 = obj1; //via cbor - std::vector const buf = nlohmann::json::to_cbor(j1); - nlohmann::json const j2 = nlohmann::json::from_cbor(buf); - T obj2; - j2.get_to(obj2); - bool ok = (obj1 == obj2); - CHECK(ok); - } + // check default value in case of missing field + Json j = Json(p1); + j.erase("name"); + j.erase("age"); + j.erase("metadata"); + j.erase("hair_color"); + const T p3 = j.template get(); + CHECK(p3.getName() == ""); + CHECK(p3.getAge() == 0); + CHECK(p3.getMetadata() == nullptr); + CHECK(p3.getHairColor() == "blue"); + } +} - { - T obj1; - nlohmann::json const j1 = obj1; //via ubjson - std::vector const buf = nlohmann::json::to_ubjson(j1); - nlohmann::json const j2 = nlohmann::json::from_ubjson(buf); - T obj2; - j2.get_to(obj2); - bool ok = (obj1 == obj2); - CHECK(ok); - } +TEST_CASE_TEMPLATE("Serialization/deserialization of classes with 26 public/private member variables via NLOHMANN_DEFINE_TYPE_INTRUSIVE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE", Pair, // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) + std::pair, + std::pair, + std::pair, + std::pair) +{ + using Json = typename Pair::first_type; + using T = typename Pair::second_type; + + SECTION("alphabet") + { + T obj1; // NOLINT(misc-const-correctness) + Json const j = obj1; + T obj2; + j.get_to(obj2); + CHECK(obj1 == obj2); } } -TEST_CASE_TEMPLATE("Serialization of non-default-constructible classes via NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE", T, - persons::person_without_default_constructor_1, - persons::person_without_default_constructor_2) +TEST_CASE_TEMPLATE("Serialization of non-default-constructible classes via NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE", Pair, // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) + std::pair, + std::pair, + std::pair, + std::pair) { + using Json = typename Pair::first_type; + using T = typename Pair::second_type; + constexpr bool is_ordered = std::is_same::value; + SECTION("person") { + // serialization of a single object + const T person{"Erik", 1}; + CHECK(Json(person).dump() == (is_ordered ? + R"({"name":"Erik","age":1})" : + R"({"age":1,"name":"Erik"})")); + + // serialization of a container with objects + std::vector const two_persons + { + {"Erik", 1}, + {"Kyle", 2} + }; + CHECK(Json(two_persons).dump() == (is_ordered ? + R"([{"name":"Erik","age":1},{"name":"Kyle","age":2}])" : + R"([{"age":1,"name":"Erik"},{"age":2,"name":"Kyle"}])")); + } +} + +TEST_CASE_TEMPLATE("Serialization of non-default-constructible classes via NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE and NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE", Pair, // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) + std::pair, + std::pair, + std::pair, + std::pair) +{ + using Json = typename Pair::first_type; + using T = typename Pair::second_type; + constexpr bool is_ordered = std::is_same::value; + + SECTION("derived person only serialize") + { + // serialization of a single object + const T person{"Erik", 1, "brown"}; + CHECK(Json(person).dump() == (is_ordered ? + R"({"name":"Erik","age":1,"hair_color":"brown"})" : + R"({"age":1,"hair_color":"brown","name":"Erik"})")); + + // serialization of a container with objects + std::vector const two_persons { - // serialization of a single object - T person{"Erik", 1}; - CHECK(json(person).dump() == "{\"age\":1,\"name\":\"Erik\"}"); - - // serialization of a container with objects - std::vector const two_persons - { - {"Erik", 1}, - {"Kyle", 2} - }; - CHECK(json(two_persons).dump() == "[{\"age\":1,\"name\":\"Erik\"},{\"age\":2,\"name\":\"Kyle\"}]"); - } + {"Erik", 1, "brown"}, + {"Kyle", 2, "black"} + }; + CHECK(Json(two_persons).dump() == (is_ordered ? + R"([{"name":"Erik","age":1,"hair_color":"brown"},{"name":"Kyle","age":2,"hair_color":"black"}])" : + R"([{"age":1,"hair_color":"brown","name":"Erik"},{"age":2,"hair_color":"black","name":"Kyle"}])")); } } diff --git a/tests/src/unit-unicode1.cpp b/tests/src/unit-unicode1.cpp index e4405c474f..174ce13955 100644 --- a/tests/src/unit-unicode1.cpp +++ b/tests/src/unit-unicode1.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -155,7 +155,7 @@ TEST_CASE("Unicode (1/5)" * doctest::skip()) SECTION("read all unicode characters") { - // read a file with all unicode characters stored as single-character + // read a file with all Unicode characters stored as single-character // strings in a JSON array std::ifstream f(TEST_DATA_DIRECTORY "/json_nlohmann_tests/all_unicode.json"); json j; diff --git a/tests/src/unit-unicode2.cpp b/tests/src/unit-unicode2.cpp index ebc29db52e..fb68815ba7 100644 --- a/tests/src/unit-unicode2.cpp +++ b/tests/src/unit-unicode2.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit-unicode3.cpp b/tests/src/unit-unicode3.cpp index dffb1cfa68..739a3dad3c 100644 --- a/tests/src/unit-unicode3.cpp +++ b/tests/src/unit-unicode3.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit-unicode4.cpp b/tests/src/unit-unicode4.cpp index 6a0e089898..f7047201c6 100644 --- a/tests/src/unit-unicode4.cpp +++ b/tests/src/unit-unicode4.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit-unicode5.cpp b/tests/src/unit-unicode5.cpp index 217d3b38d6..e4dcc21310 100644 --- a/tests/src/unit-unicode5.cpp +++ b/tests/src/unit-unicode5.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit-user_defined_input.cpp b/tests/src/unit-user_defined_input.cpp index 5483f6a0c2..56446ea94c 100644 --- a/tests/src/unit-user_defined_input.cpp +++ b/tests/src/unit-user_defined_input.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" @@ -60,12 +60,12 @@ TEST_CASE("Custom container member begin/end") { const char* data; - const char* begin() const + const char* begin() const noexcept { return data; } - const char* end() const + const char* end() const noexcept { return data + strlen(data); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) } diff --git a/tests/src/unit-windows_h.cpp b/tests/src/unit-windows_h.cpp index 9c15358544..a5c5603410 100644 --- a/tests/src/unit-windows_h.cpp +++ b/tests/src/unit-windows_h.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit-wstring.cpp b/tests/src/unit-wstring.cpp index 1aaa13ba81..dec9c1f9a7 100644 --- a/tests/src/unit-wstring.cpp +++ b/tests/src/unit-wstring.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" diff --git a/tests/src/unit.cpp b/tests/src/unit.cpp index adb39dba1c..93a934005b 100644 --- a/tests/src/unit.cpp +++ b/tests/src/unit.cpp @@ -1,9 +1,9 @@ // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN diff --git a/tests/thirdparty/doctest/doctest.h b/tests/thirdparty/doctest/doctest.h index 5c754cde08..2a29eb40bc 100644 --- a/tests/thirdparty/doctest/doctest.h +++ b/tests/thirdparty/doctest/doctest.h @@ -48,7 +48,7 @@ #define DOCTEST_VERSION_MAJOR 2 #define DOCTEST_VERSION_MINOR 4 -#define DOCTEST_VERSION_PATCH 11 +#define DOCTEST_VERSION_PATCH 12 // util we need here #define DOCTEST_TOSTR_IMPL(x) #x @@ -925,6 +925,7 @@ struct ContextOptions //!OCLINT too many fields bool no_skip; // don't skip test cases which are marked to be skipped bool gnu_file_line; // if line numbers should be surrounded with :x: and not (x): bool no_path_in_filenames; // if the path to files should be removed from the output + String strip_file_prefixes;// remove the longest matching one of these prefixes from any file paths in the output bool no_line_numbers; // if source code line numbers should be omitted from the output bool no_debug_output; // no output in the debug console when a debugger is attached bool no_skipped_summary; // don't print "skipped" in the summary !!! UNDOCUMENTED !!! @@ -1578,8 +1579,8 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP // https://github.com/catchorg/Catch2/issues/870 // https://github.com/catchorg/Catch2/issues/565 template - Expression_lhs operator<<(L&& operand) { - return Expression_lhs(static_cast(operand), m_at); + Expression_lhs operator<<(const L&& operand) { //bitfields bind to universal ref but not const rvalue ref + return Expression_lhs(static_cast(operand), m_at); } template ::value,void >::type* = nullptr> @@ -3243,6 +3244,10 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END #define DOCTEST_CONFIG_OPTIONS_PREFIX "dt-" #endif +#ifndef DOCTEST_CONFIG_OPTIONS_FILE_PREFIX_SEPARATOR +#define DOCTEST_CONFIG_OPTIONS_FILE_PREFIX_SEPARATOR ':' +#endif + #ifndef DOCTEST_THREAD_LOCAL #if defined(DOCTEST_CONFIG_NO_MULTITHREADING) || DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0)) #define DOCTEST_THREAD_LOCAL @@ -3751,7 +3756,7 @@ String::size_type String::capacity() const { } String String::substr(size_type pos, size_type cnt) && { - cnt = std::min(cnt, size() - 1 - pos); + cnt = std::min(cnt, size() - pos); char* cptr = c_str(); memmove(cptr, cptr + pos, cnt); setSize(cnt); @@ -3759,7 +3764,7 @@ String String::substr(size_type pos, size_type cnt) && { } String String::substr(size_type pos, size_type cnt) const & { - cnt = std::min(cnt, size() - 1 - pos); + cnt = std::min(cnt, size() - pos); return String{ c_str() + pos, cnt }; } @@ -3891,6 +3896,26 @@ const char* skipPathFromFilename(const char* file) { forward = back; return forward + 1; } + } else { + const auto prefixes = getContextOptions()->strip_file_prefixes; + const char separator = DOCTEST_CONFIG_OPTIONS_FILE_PREFIX_SEPARATOR; + String::size_type longest_match = 0U; + for(String::size_type pos = 0U; pos < prefixes.size(); ++pos) + { + const auto prefix_start = pos; + pos = std::min(prefixes.find(separator, prefix_start), prefixes.size()); + + const auto prefix_size = pos - prefix_start; + if(prefix_size > longest_match) + { + // TODO under DOCTEST_MSVC: does the comparison need strnicmp() to work with drive letter capitalization? + if(0 == std::strncmp(prefixes.c_str() + prefix_start, file, prefix_size)) + { + longest_match = prefix_size; + } + } + } + return &file[longest_match]; } #endif // DOCTEST_CONFIG_DISABLE return file; @@ -4472,11 +4497,12 @@ namespace { String translateActiveException() { #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS - String res; auto& translators = getExceptionTranslators(); - for(auto& curr : translators) - if(curr->translate(res)) + for(auto& curr : translators) { + String res; + if (curr->translate(res)) return res; + } // clang-format off DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wcatch-value") try { @@ -6181,6 +6207,8 @@ namespace { << Whitespace(sizePrefixDisplay*1) << ":n: vs (n): for line numbers in output\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "npf, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-path-filenames= " << Whitespace(sizePrefixDisplay*1) << "only filenames and no paths in output\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "spp, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "skip-path-prefixes= " + << Whitespace(sizePrefixDisplay*1) << "whenever file paths start with this prefix, remove it from the output\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nln, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-line-numbers= " << Whitespace(sizePrefixDisplay*1) << "0 instead of real line numbers in output\n"; // ================================================================================== << 79 @@ -6685,6 +6713,7 @@ void Context::parseArgs(int argc, const char* const* argv, bool withDefaults) { DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-skip", "ns", no_skip, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("gnu-file-line", "gfl", gnu_file_line, !bool(DOCTEST_MSVC)); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-path-filenames", "npf", no_path_in_filenames, false); + DOCTEST_PARSE_STR_OPTION("strip-file-prefixes", "sfp", strip_file_prefixes, ""); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-line-numbers", "nln", no_line_numbers, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-debug-output", "ndo", no_debug_output, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-skipped-summary", "nss", no_skipped_summary, false); diff --git a/tests/thirdparty/imapdl/filterbr.py b/tests/thirdparty/imapdl/filterbr.py index e8714c7ef1..49e37aa606 100755 --- a/tests/thirdparty/imapdl/filterbr.py +++ b/tests/thirdparty/imapdl/filterbr.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 # 2017, Georg Sauthoff , GPLv3 +# 2022, Alexander Stohr, ZF Friedrichshafen AG: benign handling of UTF-8 violations import sys @@ -49,7 +50,7 @@ def cond_lines(lines): p = n continue if (p == 0 or not line[p-1].isalpha()) \ - and (p+2 == len(line) or not line[p+2].isalpha()): + and (p+2 == len(line) or not line[p+2].isalpha()): do_yield = True state = 1 p += 2 @@ -93,7 +94,9 @@ def filter_lcov_trace(lines): yield line def filter_lcov_trace_file(s_filename, d_file): - with open(s_filename) as f: + # encoding is anyways the python default: utf-8 + # standard error is "strict"; python style escaping is "backslashreplace"; alternate benign handler is "surrogateescape" + with open(s_filename, encoding="utf-8", errors="backslashreplace") as f: for l in filter_lcov_trace(f): print(l, end='', file=d_file) @@ -106,4 +109,3 @@ def filter_lcov_trace_file(s_filename, d_file): #with open(sys.argv[1]) as f: # for l in skip_comments(f): # print(l) - diff --git a/tools/amalgamate/README.md b/tools/amalgamate/README.md index 975ca0bdf8..8e088c4d2c 100644 --- a/tools/amalgamate/README.md +++ b/tools/amalgamate/README.md @@ -1,4 +1,3 @@ - # amalgamate.py - Amalgamate C source and header files Origin: https://bitbucket.org/erikedlund/amalgamate diff --git a/tools/astyle/.astylerc b/tools/astyle/.astylerc new file mode 100644 index 0000000000..580a3fca50 --- /dev/null +++ b/tools/astyle/.astylerc @@ -0,0 +1,80 @@ +# Configuration for Artistic Style +# see https://astyle.sourceforge.net/astyle.html + +####################### +# Brace Style Options # +####################### + +# use Allman style for braces +--style=allman + +############### +# Tab Options # +############### + +# indent using 4 spaces +--indent=spaces=4 + +####################### +# Indentation Options # +####################### + +# indent access modifiers one half indent +--indent-modifiers + +# indent switch cases to the switch block +--indent-switches + +# indent preprocessor blocks +--indent-preproc-block + +# indent preprocessor defines +--indent-preproc-define + +# indent C++ comments +--indent-col1-comments + +################### +# Padding Options # +################### + +# insert space padding around operators +--pad-oper + +# insert space between if/for/while... and the following parentheses +--pad-header + +# attach the pointer to the variable type (left) +--align-pointer=type + +# attach the reference to the variable type (left) +--align-reference=type + +###################### +# Formatting Options # +###################### + +# add braces to unbraced one line conditional statements +--add-braces + +# convert tabs to spaces +--convert-tabs + +# closes whitespace between the ending angle brackets of template definitions +--close-templates + +################# +# Other Options # +################# + +# do not create backup files +--suffix=none + +# preserve the original file date +--preserve-date + +# display only the files that have been formatted +--formatted + +# for the linux (LF) line end style +--lineend=linux diff --git a/tools/astyle/requirements.txt b/tools/astyle/requirements.txt new file mode 100644 index 0000000000..99458a860c --- /dev/null +++ b/tools/astyle/requirements.txt @@ -0,0 +1 @@ +astyle==3.4.13 diff --git a/tools/gdb_pretty_printer/README.md b/tools/gdb_pretty_printer/README.md index c1a3651d4f..4a95776df4 100644 --- a/tools/gdb_pretty_printer/README.md +++ b/tools/gdb_pretty_printer/README.md @@ -1,6 +1,7 @@ # GDB Pretty Printer -File [nlohmann-json.py](nlohmann-json.py) contains a pretty printer for GDB for JSON values of this library. It was originally published as [Gist](https://gist.github.com/ssbssa/60da5339c6e6036b2afce17de06050ea#file-nlohmann-json-py) by [Hannes Domani](https://github.com/ssbssa). +File [nlohmann-json.py](nlohmann-json.py) contains a pretty printer for GDB for JSON values of this library. It was +originally published as [Gist](https://gist.github.com/ssbssa/60da5339c6e6036b2afce17de06050ea#file-nlohmann-json-py) by [Hannes Domani](https://github.com/ssbssa). ## How to use diff --git a/tools/gdb_pretty_printer/nlohmann-json.py b/tools/gdb_pretty_printer/nlohmann-json.py index 3984f592fa..2c120befd9 100644 --- a/tools/gdb_pretty_printer/nlohmann-json.py +++ b/tools/gdb_pretty_printer/nlohmann-json.py @@ -15,18 +15,21 @@ def to_string(self): def json_lookup_function(val): if m := ns_pattern.fullmatch(str(val.type.strip_typedefs().name)): - name = m.group('name') - if name and name.startswith('basic_json<') and name.endswith('>'): - m = ns_pattern.fullmatch(str(val["m_data"]['m_type'])) - t = m.group('name') - if t and t.startswith('detail::value_t::'): - try: - union_val = val["m_data"]['m_value'][t.removeprefix('detail::value_t::')] - if union_val.type.code == gdb.TYPE_CODE_PTR: - return gdb.default_visualizer(union_val.dereference()) - else: - return JsonValuePrinter(union_val) - except Exception: - return JsonValuePrinter(val["m_data"]['m_type']) + name = m.group('name') + if name and name.startswith('basic_json<') and name.endswith('>'): + m_data = val['m_data'] + m_type = m_data['m_type'] + m = ns_pattern.fullmatch(str(m_type)) + t = m.group('name') + prefix = 'detail::value_t::' + if t and t.startswith(prefix): + try: + union_val = m_data['m_value'][t.replace(prefix, '', 1)] + if union_val.type.code == gdb.TYPE_CODE_PTR: + return gdb.default_visualizer(union_val.dereference()) + else: + return JsonValuePrinter(union_val) + except Exception: + return JsonValuePrinter(m_type) gdb.pretty_printers.append(json_lookup_function) diff --git a/tools/generate_natvis/README.md b/tools/generate_natvis/README.md index b4d3a7d7f1..e11f29eecb 100644 --- a/tools/generate_natvis/README.md +++ b/tools/generate_natvis/README.md @@ -1,10 +1,9 @@ -generate_natvis.py -================== +# generate_natvis.py Generate the Natvis debugger visualization file for all supported namespace combinations. ## Usage -``` +```shell ./generate_natvis.py --version X.Y.Z output_directory/ ``` diff --git a/tools/generate_natvis/requirements.txt b/tools/generate_natvis/requirements.txt new file mode 100644 index 0000000000..6bbf6aa1d4 --- /dev/null +++ b/tools/generate_natvis/requirements.txt @@ -0,0 +1 @@ +jinja2==3.1.6 diff --git a/tools/serve_header/README.md b/tools/serve_header/README.md index 123fd4479c..0d0ed69f63 100644 --- a/tools/serve_header/README.md +++ b/tools/serve_header/README.md @@ -17,7 +17,7 @@ The header file is automatically amalgamated on demand. (see `tools/serve_header/requirements.txt`) 2. To serve the header over HTTPS (which is required by Compiler Explorer at this time), a certificate is needed. - The recommended method for creating a locally-trusted certificate is to use [`mkcert`](https://github.com/FiloSottile/mkcert). + The recommended method for creating a locally trusted certificate is to use [`mkcert`](https://github.com/FiloSottile/mkcert). - Install the `mkcert` certificate authority into your trust store(s): ``` $ mkcert -install diff --git a/tools/serve_header/requirements.txt b/tools/serve_header/requirements.txt index d32eed3bd5..0c6ce0d81a 100644 --- a/tools/serve_header/requirements.txt +++ b/tools/serve_header/requirements.txt @@ -1,2 +1,2 @@ -PyYAML==6.0 -watchdog==2.1.7 +PyYAML==6.0.3 +watchdog==6.0.0 diff --git a/tools/serve_header/serve_header.py b/tools/serve_header/serve_header.py index 9844e68009..e2da2dad0b 100755 --- a/tools/serve_header/serve_header.py +++ b/tools/serve_header/serve_header.py @@ -231,6 +231,12 @@ def on_any_event(self, event): elif event.event_type == 'deleted': # check for deleted working trees self.rescan(path) + elif event.event_type == 'moved': + # handle moved directories - treat source as deleted and dest as created + self.rescan(path) + if hasattr(event, 'dest_path'): + dest_path = os.path.abspath(event.dest_path) + self.created_bucket.add_dir(dest_path) elif event.event_type == 'closed': with self.tree_lock: for tree in self.trees: @@ -265,7 +271,7 @@ def translate_path(self, path): def send_head(self): # check if the translated path matches a working tree - # and fullfill the request; otherwise, send 404 + # and fulfill the request; otherwise, send 404 path = self.translate_path(self.path) self.worktree = self.worktrees.find(path) if self.worktree is not None: diff --git a/wsjcpp.yml b/wsjcpp.yml deleted file mode 100644 index e6443cf829..0000000000 --- a/wsjcpp.yml +++ /dev/null @@ -1,26 +0,0 @@ -wsjcpp_version: "v0.1.1" -cmake_minimum_required: "3.0" -cmake_cxx_standard: "11" -name: "nlohmann/json" -version: "v3.11.3" -description: "JSON for Modern C++" -issues: "https://github.com/nlohmann/json/issues" -keywords: - - "c++" - - "json" - -repositories: - - type: main - url: "https://github.com/nlohmann/json" - -authors: - - name: "Niels Lohmann" - email: "mail@nlohmann.me" - -distribution: - - source-file: "single_include/nlohmann/json.hpp" - target-file: "json.hpp" - type: "source-code" - - source-file: "single_include/nlohmann/json_fwd.hpp" - target-file: "json_fwd.hpp" - type: "source-code"