Compare commits

..
Author SHA1 Message Date
Niels Lohmann 7d22d865dd Merge branch 'develop' into claude/todo-191-plan-508110
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-28 22:22:46 +02:00
Niels Lohmann 633de8e44b Fix CI: clang-tidy and GCC -Wnoexcept in the locale test (#5613)
#5597 was merged before all of its CI jobs had run, and two of them fail
on develop now, and so on every pull request:

- ci_clang_tidy: cert-err33-c for the two std::setlocale(LC_NUMERIC, "C")
  calls whose result was discarded. Check the result, like the other
  resets in the file.
- ci_test_standards_gcc (20) with GCC 16: -Wnoexcept for the two parser
  callbacks, which cannot throw but were not declared noexcept.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-28 22:20:43 +02:00
Niels Lohmann 4c73319d3b Use one-line module docstrings in the API checker scripts
Codacy runs two docstring checkers with opposite rules for module
docstrings: with the summary on the first line it reported D213, with
the summary on the second line it reports D212. A one-line docstring
satisfies both. Keep the summary as the docstring and move the details
into a comment below it; nothing reads the module docstrings.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-28 18:42:34 +02:00
Niels Lohmann fc03b9912e Look up the locale decimal point at conversion time, not lexer construction (#5597)
* Look up the locale decimal point at conversion time, not lexer construction

The lexer read localeconv()->decimal_point once in its constructor and wrote
that character into token_buffer in place of '.'. The strtod fallback then
used the locale current at conversion time, so an LC_NUMERIC change in
between (parser callback, SAX handler, another thread) truncated the value
in release builds and fired the endptr assertion in debug builds.

token_buffer now always holds '.'. Only the strtof/strtod/strtold fallback
depends on the locale: it looks up the decimal point right before the call,
restores '.' afterwards, and repeats the conversion if the locale changed in
between. As a side effect, std::from_chars and Clinger's fast path now also
apply under locales whose decimal point is not '.'.

Fixes #5198

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Stop the strtod retry loop when the decimal point is unchanged

convert_float_locale_aware() repeated the conversion until strtod
consumed the whole token, assuming an early stop can only mean a locale
change. Under a locale whose decimal point is not a single character
(e.g. the two-byte U+066B of ar_EG.UTF-8, ar_SA.UTF-8, or fa_IR.UTF-8,
all available on macOS), the in-place substitution can never succeed,
so parsing any float that reaches the strtod fallback (for example
3.14159265358979323846 at C++11) hung forever. Before this branch, the
same input was truncated.

Retry only if the decimal point changed since the previous attempt;
otherwise keep the value strtod parsed so far, as before. Add a test
that parses such numbers under a multi-byte decimal point locale; it
hangs without this change.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix -Weffc++ errors in the #5198 locale test

GCC's -Weffc++ (an error in ci_test_gcc and ci_test_standards_gcc)
rejected LocaleSwitchingSax: it has a pointer data member but does not
declare its copy operations, and its vectors are not initialized in the
member initializer list. Store the locale name as a std::string and give
the vectors brace initializers, like SaxEventLogger in
unit-deserialization.cpp.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-28 17:56:11 +02:00
Niels Lohmann 9e1a09eec0 Name the key type when rejecting non-string CBOR/MessagePack map keys (#5594)
* Name the key type when rejecting non-string CBOR/MessagePack map keys

CBOR and MessagePack allow map keys of any type, but JSON object keys
are always strings, so such maps are rejected. The error so far was the
one for a malformed string (e.g. "expected length specification
(0xA0-0xBF, 0xD9-0xDB); last byte: 0xC0" for a nil key), which does not
tell the user what went wrong. Report the type of the key instead:

  syntax error while parsing MessagePack object key: only string keys
  are supported, but found nil; last byte: 0xC0

The exception id (parse_error.113) and type are unchanged. Malformed
string keys and a missing key keep their previous messages. Document
the restriction on the CBOR and MessagePack pages.

Refs #2766, #3381

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Point the MessagePack key note to the spec's profile section

The note linked to "Serialization: type to format conversion", which says nothing about key types. Restricting map keys to strings is only mentioned in the "Profile" section (under "Future discussion") as an example of a JSON-compatible profile, so link there and describe it as such instead of as a permission.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-28 17:51:07 +02:00
Niels Lohmann da5097abcd Restore the CI spelling of json_pointer's operator string_t in the API surface
The develop merge regenerated api_surface.json on macOS, whose libclang
spells the name of the deprecated conversion operator as
"operator nlohmann::json_pointer::string_t_helper<...>::type". CI's
pinned libclang 18.1.1 on Linux spells it
"operator typename string_t_helper<...>::type", so check_api_docs
reported the file as out of date. This restores CI's spelling, which is
byte-identical to the file CI regenerated.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-27 21:37:14 +02:00
Niels Lohmann 2319f6e6f9 Address Codacy findings in the API checker scripts
Put multi-line docstring summaries on their own line, as a single
sentence followed by a blank line (pydocstyle D205, D209, D213, D415).

Annotate the subprocess import and calls with nosec: they only run
fixed argument lists, never through a shell (Bandit B404, B603, B607).
Do the same for the three broad except clauses in extract_api.py,
which deliberately fall through to the next libclang candidate or skip
an unresolvable alias (Bandit B110, B112).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-27 18:09:03 +02:00
Niels Lohmann 9a0d1c0c47 Merge branch 'develop' into claude/todo-191-plan-508110
Resolve the conflict in docs/mkdocs/docs/home/architecture.md by taking
develop's rewritten page and pointing its ordered_map links at the moved
api/ordered_map/index.md. Also update the ordered_map.md links develop
added in ordered_json.md, object_order.md, and template_parameters.md.

Regenerate api_surface.json for the new BON8 functions and the changed
comparison operator. Restrict the documented_non_public leak check to
@sa URLs into json.nlohmann.me: develop now uses @sa to link GitHub
issues from private members such as copy_structured, which is not a
documentation leak.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-27 18:07:00 +02:00
Niels Lohmann 1bbb5d400a Regenerate api_surface.json after merging develop
develop added JSON_HEDLEY_WARN_UNUSED_RESULT to a number of basic_json
members (#5520) and reworked operator<<'s output adapter, so the
committed API surface no longer matched what extract_api.py produces and
the "Check for uncommitted API surface changes" step failed.

This is the patch the workflow itself uploads for that purpose; the
changes are limited to recorded signatures plus the way libclang spells
json_pointer's conversion operator.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-15 10:06:55 +02:00
Niels Lohmann 08d18d5a82 Merge remote-tracking branch 'origin/develop' into HEAD 2026-09-14 21:26:29 +02:00
Niels LohmannandClaude Sonnet 5 a27065bd12 Regenerate api_surface.json after merging develop
develop gained iterator+sentinel support for accept()/parse()/sax_parse()/
from_cbor()/from_msgpack()/from_ubjson()/from_bjdata()/from_bson() (#5205)
since this branch was created, which check_api_docs.yml's drift check caught
immediately: GitHub's pull_request trigger checks out the PR-vs-base merge
commit, not the PR branch in isolation, so CI was comparing the committed
api_surface.json (generated before that upstream change existed) against a
fresh extraction of a json.hpp that already had it.

This superseded an earlier, incorrect diagnosis of the same symptom (a
JSON_HAS_RANGES cross-environment pin) -- that fix remains in place since it's
still a real, independent determinism improvement for three other
JSON_HAS_RANGES-gated locations in type_traits.hpp, but it wasn't the actual
cause of this particular mismatch: these new overloads turned out to be
unconditional (SFINAE-gated via can_compare_ne, not preprocessor-gated), so
no environment-detection difference was involved here at all.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-11 23:45:46 +02:00
Niels Lohmann 3940f4b730 Merge remote-tracking branch 'origin/develop' into claude/todo-191-plan-508110 2026-07-11 23:43:30 +02:00
Niels LohmannandClaude Sonnet 5 0663907b68 Fix cross-environment API-surface drift and lint findings from CI
extract_api.py's own extraction wasn't deterministic across machines, which
CI's drift check caught immediately: JSON_HAS_RANGES auto-detects via the
standard library's __cpp_lib_ranges feature-test macro, which isn't reliably
gated to C++20 mode by every stdlib -- undefined under -std=c++17 with macOS's
libc++, but defined under the identical flag with the Ubuntu stdlib CI uses,
so parse()/accept()/from_*() extracted different signatures purely depending
on which machine ran the extraction. Pinned to -DJSON_HAS_RANGES=0: the
deterministic and safe choice, since pinning to 1 was tried first and found to
fail to parse on a stdlib without full <ranges> support even when the macro
claims otherwise.

Also found and fixed a second, independent source of the same class of drift:
get_identity_name() used cursor.spelling verbatim for CONVERSION_FUNCTION
cursors, which libclang renders as its own internally-canonicalized form of
the return type rather than what's literally written. Confirmed for
json_pointer::operator string_t() spelling differently on two machines
pinned to the identical libclang==18.1.1 wheel, with the JSON_HAS_RANGES fix
above ruled out as the cause. Now derived from the cursor's own raw source
text instead, immune to libclang's dependent-type resolution differences and
incidentally more readable than the libclang-internal forms it replaces.

Bumped SURFACE_FORMAT_VERSION to 3 and regenerated all 27 history snapshots
and the committed api_surface.json; both fixes are documented in
tools/api_checker/history/README.md's format-history log.

Also fixes diff_api.py's format_version guard, which only compared the two
loaded surfaces against each other and never against SURFACE_FORMAT_VERSION
(what this build actually understands) -- two surfaces on the same,
newer-than-expected format_version would have silently passed the guard.

Remaining fixes are the concretely actionable findings from Codacy's review
of the new tools/api_checker/ files: unused imports/variables, a stray
f-string with no placeholders. Left the docstring-formatting nitpicks
(pydocstyle D2xx/D4xx) and generic subprocess-usage notices alone -- the
former has no established convention elsewhere in this codebase's Python
tooling to conform to, and the latter are inherent to a dev tool that shells
out to git/clang with developer-controlled arguments, not user input.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-11 18:59:19 +02:00
Niels LohmannandClaude Sonnet 5 f23b3c63a2 Add AST-based public API checker and fix documentation gaps it found (#3691)
Adds tools/api_checker/: extract_api.py derives the public API surface directly
from the libclang AST (independent of documentation status), check_docs.py flags
public entries missing @sa links (and @sa on non-public ones), diff_api.py does
an overload-aware breaking/feature diff between two refs, check_macros.py cross-
checks documented macros against #define sites, and snapshot_release.py backfills
immutable per-release surface snapshots into tools/api_checker/history/ (v3.1.0
through v3.12.0) so diff_api.py can compare releases without live extraction.
POLICY.md documents what counts as public API and what stability is guaranteed.

Running this tooling against the current tree found and fixed a real documentation
backlog: ~25 new API doc pages (ordered_map's methods, json_sax's ctor/dtor/
operator=, byte_container_with_subtype's comparison operators, several orphaned
type aliases), each with a compiled and output-verified example, plus missing
@sa comments and stale/incorrect Version History entries on several existing
pages (found by diffing consecutive release pairs and checking whether the
resulting change was actually reflected in the target page's history section).

Also adds docs/home/api_changes.md, a per-release, per-function reference of
public API changes (v3.1.0 through v3.12.0) generated from the history/
snapshots, complementing (not replacing) the existing release notes.

Along the way, found and fixed several extractor bugs by testing against real
release tags rather than trusting the algorithm in isolation -- most notably an
identity-key scheme based on libclang's USR that encoded the enclosing class
template's own arity, and a since-renamed ABI inline-namespace pattern
(json_v3_11_0 vs. today's json_abi_v3_11_2) that neither of two earlier regex
attempts stripped correctly. Both are documented in extract_api.py's docstrings
and tools/api_checker/history/README.md so the failure mode doesn't recur
silently.

.github/workflows/check_api_docs.yml runs extract_api.py + check_docs.py in CI,
advisory-only for now (documented backlog may not be at zero for entities this
PR didn't touch), plus a blocking drift check on the committed
tools/api_checker/api_surface.json.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-11 18:59:00 +02:00
141 changed files with 73224 additions and 213 deletions
+81
View File
@@ -0,0 +1,81 @@
name: "Check API documentation"
on:
pull_request:
permissions:
contents: read
jobs:
check_api_docs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit
- name: Checkout pull request
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Install clang
# Used only as a subprocess for `clang++ -E -v` system-include-path discovery in
# extract_api.py; it does not need to version-match the pinned libclang pip wheel
# below, which does the actual AST parsing. Do not "fix" this to be version-matched.
run: sudo apt-get update && sudo apt-get install -y clang
- name: Install Python dependencies
run: pip install -r tools/api_checker/requirements.txt
- name: Extract API and regenerate the committed surface file
run: |
python3 tools/api_checker/extract_api.py \
--header include/nlohmann/json.hpp \
--include include \
--output /tmp/api_snapshot.json \
--surface-output tools/api_checker/api_surface.json
- name: "Check API documentation (Phase 1: advisory)"
# Surfaces missing/broken @sa links without failing the job while the backlog from the
# initial AST-based rollout is burned down. See tools/api_checker/POLICY.md and the PR
# that introduced this workflow for the two-phase rollout plan.
continue-on-error: true
run: |
python3 tools/api_checker/check_docs.py \
--snapshot /tmp/api_snapshot.json
- name: Check macro documentation (advisory only)
# Cross-checks docs/mkdocs/docs/api/macros/ pages against #define sites. Only checks the
# documented-macro-still-exists direction; never blocks CI. See POLICY.md.
run: python3 tools/api_checker/check_macros.py
- name: Check for uncommitted API surface changes
id: diff
run: |
mkdir -p ${{ github.workspace }}/patch
git diff --patch --no-color -- tools/api_checker/api_surface.json > ${{ github.workspace }}/patch/api_surface.patch
if [ -s ${{ github.workspace }}/patch/api_surface.patch ]; then
echo "tools/api_checker/api_surface.json is out of date. Diff:"
cat ${{ github.workspace }}/patch/api_surface.patch
echo "has_diff=true" >> "$GITHUB_OUTPUT"
else
echo "has_diff=false" >> "$GITHUB_OUTPUT"
fi
# Uploaded so contributors can fix their PR with `git apply api_surface.patch`
# instead of installing libclang locally.
- name: Upload patch
if: steps.diff.outputs.has_diff == 'true'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: api-surface-patch
path: patch/api_surface.patch
- name: Fail if API surface file is not up to date
# Unlike the doc-backlog check above, this is purely mechanical regeneration with no
# backlog to phase in -- blocking from the start, matching check_amalgamation.yml's
# precedent. Contributors who add/remove/rename public API must regenerate and commit
# tools/api_checker/api_surface.json as part of their PR.
if: steps.diff.outputs.has_diff == 'true'
run: exit 1
+5
View File
@@ -3,6 +3,7 @@
*.gcno *.gcno
*.gcda *.gcda
.DS_Store .DS_Store
__pycache__/
/.idea /.idea
/cmake-build-* /cmake-build-*
@@ -43,5 +44,9 @@ venv
nlohmann_json.spdx nlohmann_json.spdx
# api_checker: ephemeral, location/doc-status-sensitive working file (not the committed
# release-tracking artifact -- see tools/api_checker/api_surface.json for that)
/tools/api_checker/api_snapshot.json
# Bazel-related # Bazel-related
MODULE.bazel.lock MODULE.bazel.lock
+1 -1
View File
@@ -106,7 +106,7 @@ Thanks everyone!
: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). :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/) or have a look at the [quick reference](#quick-reference) below. :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. The public API surface is derived mechanically and checked for documentation coverage by the tooling in [`tools/api_checker/`](tools/api_checker/), whose [POLICY.md](tools/api_checker/POLICY.md) defines what counts as public API and what stability is guaranteed.
: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. :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.
@@ -420,7 +420,9 @@ basic_json(basic_json&& other) noexcept;
1. Since version 1.0.0. 1. Since version 1.0.0.
2. Since version 1.0.0. 2. Since version 1.0.0.
3. Since version 2.1.0. 3. Since version 2.1.0.
4. Since version 3.2.0. 4. Since version 3.2.0. Also initializes the position reported by
[`start_pos()`](start_pos.md)/[`end_pos()`](end_pos.md) from `val` when
[`JSON_DIAGNOSTIC_POSITIONS`](../macros/json_diagnostic_positions.md) is enabled, since version 3.12.0.
5. Since version 1.0.0. 5. Since version 1.0.0.
6. Since version 1.0.0. 6. Since version 1.0.0.
7. Since version 1.0.0. 7. Since version 1.0.0.
@@ -0,0 +1,38 @@
# <small>nlohmann::basic_json::</small>bjdata_version_t
```cpp
enum class bjdata_version_t
{
draft2,
draft3,
};
```
This enumeration is used in the [`to_bjdata`](to_bjdata.md) function to choose which draft version of
the BJData specification to encode ND-array extensions for:
draft2
: encode using the BJData Draft 2 ND-array format
draft3
: encode using the BJData Draft 3 ND-array format
## Examples
??? example
The example shows how `bjdata_version_t` selects the BJData draft used by `to_bjdata`.
```cpp
--8<-- "examples/bjdata_version_t.cpp"
```
Output:
```
--8<-- "examples/bjdata_version_t.output"
```
## Version history
- Added in version 3.12.0.
+2 -2
View File
@@ -80,8 +80,8 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
the end of the 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 - 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 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 a map key, - Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a map key is not a string (keys of other
but not found types are not supported, as JSON object keys are always strings) or a string is malformed
## Complexity ## Complexity
@@ -73,8 +73,8 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
the end of the 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 - 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 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 a map key, - Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a map key is not a string (keys of other
but not found types are not supported, as JSON object keys are always strings) or a string is malformed
## Complexity ## Complexity
@@ -0,0 +1,32 @@
# <small>nlohmann::basic_json::</small>initializer_list_t
```cpp
using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
```
The type used for the initializer-list [constructor](basic_json.md) (overload 5) and for functions
such as [`operator=`](operator=.md) that accept a braced-init-list of JSON values. Each element wraps a
`basic_json` value or something convertible to one, deferring the decision of whether the list should be
parsed as a JSON array or a JSON object to the constructor itself.
See the [constructor](basic_json.md) documentation for how `initializer_list_t` values are interpreted.
## Examples
??? example
The example shows how an `initializer_list_t` is used to construct a JSON value.
```cpp
--8<-- "examples/initializer_list_t.cpp"
```
Output:
```
--8<-- "examples/initializer_list_t.output"
```
## Version history
- Since version 1.0.0.
@@ -0,0 +1,31 @@
# <small>nlohmann::basic_json::</small>json_sax_t
```cpp
using json_sax_t = json_sax<basic_json>;
```
The [`json_sax`](../json_sax/index.md) interface bound to this `basic_json` specialization, i.e. with
`BasicJsonType` fixed to `basic_json`. Used as the SAX interface type by [`sax_parse`](sax_parse.md) and
other SAX-based parsing functions.
See [`nlohmann::json_sax`](../json_sax/index.md) for more information.
## Examples
??? example
The example shows the type `json_sax_t`.
```cpp
--8<-- "examples/json_sax_t.cpp"
```
Output:
```
--8<-- "examples/json_sax_t.output"
```
## Version history
- Added in version 3.2.0.
@@ -51,3 +51,5 @@ Linear.
## Version history ## Version history
- Added in version 1.0.0. - Added in version 1.0.0.
- The `noexcept` specification was extended to also depend on
[`json_base_class_t`](json_base_class_t.md)'s move-assignment in version 3.11.3.
+3 -11
View File
@@ -86,9 +86,6 @@ Strong exception safety: if an exception occurs, the original value stays intact
- Throws [`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) if an array index in the passed - Throws [`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) if an array index in the passed
JSON pointer `ptr` exceeds the range of `size_type` (e.g., on 32-bit platforms). JSON pointer `ptr` exceeds the range of `size_type` (e.g., on 32-bit platforms).
For the **const** version, an object key or array index in `ptr` that does not exist is not reported by an
exception, but is undefined behavior (see the notes below). Use [`at`](at.md) for checked access.
## Complexity ## Complexity
1. Constant if `idx` is in the range of the array. Otherwise, linear in `idx - size()`. 1. Constant if `idx` is in the range of the array. Otherwise, linear in `idx - size()`.
@@ -103,12 +100,9 @@ Strong exception safety: if an exception occurs, the original value stays intact
The following cases apply to the **const** overloads; the non-const overloads instead insert the missing element The following cases apply to the **const** overloads; the non-const overloads instead insert the missing element
(see the notes below). (see the notes below).
1. If the element at index `idx` does not exist, the behavior is undefined and is **guarded by a 1. If the element at index `idx` does not exist, the behavior is undefined.
[runtime assertion](../../features/assertions.md)**!
2. If the element with key `key` does not exist, the behavior is undefined and is **guarded by a 2. If the element with key `key` does not exist, the behavior is undefined and is **guarded by a
[runtime assertion](../../features/assertions.md)**! [runtime assertion](../../features/assertions.md)**!
3. If the JSON pointer `ptr` refers to an object key or an array index that does not exist, the behavior is
undefined and is **guarded by a [runtime assertion](../../features/assertions.md)**!
1. The non-const version may add values: If `idx` is beyond the range of the array (i.e., `idx >= size()`), then the 1. The non-const version may add values: If `idx` is beyond the range of the array (i.e., `idx >= size()`), then the
array is silently filled up with `#!json null` values to make `idx` a valid reference to the last stored element. In array is silently filled up with `#!json null` values to make `idx` a valid reference to the last stored element. In
@@ -263,11 +257,9 @@ Strong exception safety: if an exception occurs, the original value stays intact
## Version history ## Version history
1. Added in version 1.0.0. A missing index in the const version is guarded by a runtime assertion since version 1. Added in version 1.0.0.
3.13.0.
2. Added in version 1.0.0. Added overloads for `T* key` in version 1.1.0. Removed overloads for `T* key` (replaced by 3) 2. Added in version 1.0.0. Added overloads for `T* key` in version 1.1.0. Removed overloads for `T* key` (replaced by 3)
in version 3.11.0. in version 3.11.0.
3. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as 3. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as
already supported by [`at`](at.md), [`value`](value.md), [`find`](find.md), and other lookup functions. already supported by [`at`](at.md), [`value`](value.md), [`find`](find.md), and other lookup functions.
4. Added in version 2.0.0. A missing array index in the const version is guarded by a runtime assertion since 4. Added in version 2.0.0.
version 3.13.0.
@@ -85,3 +85,8 @@ Linear in the size of the JSON value.
- Since version 1.0.0. - Since version 1.0.0.
- Macros `JSON_EXPLICIT`/[`JSON_USE_IMPLICIT_CONVERSIONS`](../macros/json_use_implicit_conversions.md) added - Macros `JSON_EXPLICIT`/[`JSON_USE_IMPLICIT_CONVERSIONS`](../macros/json_use_implicit_conversions.md) added
in version 3.9.0. in version 3.9.0.
- The exclusion of `std::any` from this conversion became conditional on
[`JSON_HAS_STATIC_RTTI`](../macros/json_has_static_rtti.md) in version 3.11.3.
- `std::optional<T>` excluded from this conversion in version 3.13.0; use
[`get<std::optional<T>>()`](get.md)/[`get_to()`](get_to.md) instead (see
[Converting values](../../features/conversions.md)).
@@ -0,0 +1,32 @@
# <small>nlohmann::byte_container_with_subtype::</small>container_type
```cpp
using container_type = BinaryType;
```
The type of the underlying binary container, forwarded from the `BinaryType` template parameter that
`byte_container_with_subtype` is instantiated with. `byte_container_with_subtype` publicly inherits from
`container_type`.
See [`basic_json::binary_t`](../basic_json/binary_t.md) for the type typically used to instantiate
`BinaryType`.
## Examples
??? example
The example shows the type `container_type`.
```cpp
--8<-- "examples/byte_container_with_subtype__container_type.cpp"
```
Output:
```
--8<-- "examples/byte_container_with_subtype__container_type.output"
```
## Version history
- Since version 3.8.0.
@@ -0,0 +1,45 @@
# <small>nlohmann::byte_container_with_subtype::</small>operator==
```cpp
bool operator==(const byte_container_with_subtype& rhs) const;
```
Compares two `byte_container_with_subtype` values for equality by comparing the underlying binary
container, the subtype, and whether a subtype is set.
## Parameters
`rhs` (in)
: value to compare `*this` against
## Return value
whether `*this` and `rhs` are equal
## Exception safety
No-throw guarantee: this function never throws exceptions.
## Complexity
Linear in the size of the underlying binary container.
## Examples
??? example
The example demonstrates comparing `byte_container_with_subtype` values.
```cpp
--8<-- "examples/byte_container_with_subtype__operator_eq.cpp"
```
Output:
```
--8<-- "examples/byte_container_with_subtype__operator_eq.output"
```
## Version history
- Since version 3.8.0.
@@ -0,0 +1,45 @@
# <small>nlohmann::byte_container_with_subtype::</small>operator!=
```cpp
bool operator!=(const byte_container_with_subtype& rhs) const;
```
Compares two `byte_container_with_subtype` values for inequality. Implemented as the negation of
[`operator==`](operator_eq.md).
## Parameters
`rhs` (in)
: value to compare `*this` against
## Return value
whether `*this` and `rhs` are not equal
## Exception safety
No-throw guarantee: this function never throws exceptions.
## Complexity
Linear in the size of the underlying binary container.
## Examples
??? example
The example demonstrates comparing `byte_container_with_subtype` values.
```cpp
--8<-- "examples/byte_container_with_subtype__operator_ne.cpp"
```
Output:
```
--8<-- "examples/byte_container_with_subtype__operator_ne.output"
```
## Version history
- Since version 3.8.0.
@@ -0,0 +1,28 @@
# <small>nlohmann::byte_container_with_subtype::</small>subtype_type
```cpp
using subtype_type = std::uint64_t;
```
The type used to store the optional binary subtype tag. See [`subtype`](subtype.md) and
[`set_subtype`](set_subtype.md).
## Examples
??? example
The example shows the type `subtype_type`.
```cpp
--8<-- "examples/byte_container_with_subtype__subtype_type.cpp"
```
Output:
```
--8<-- "examples/byte_container_with_subtype__subtype_type.output"
```
## Version history
- Since version 3.8.0.
+30
View File
@@ -0,0 +1,30 @@
# <small>nlohmann::json_sax::</small>binary_t
```cpp
using binary_t = typename BasicJsonType::binary_t;
```
The type used by the [`binary`](binary.md) callback for JSON binary values, forwarded from the
`BasicJsonType` template parameter.
See [`basic_json::binary_t`](../basic_json/binary_t.md) for more information.
## Examples
??? example
The example shows the type `binary_t` and its relation to `basic_json::binary_t`.
```cpp
--8<-- "examples/json_sax__binary_t.cpp"
```
Output:
```
--8<-- "examples/json_sax__binary_t.output"
```
## Version history
- Added in version 3.8.0.
+33
View File
@@ -0,0 +1,33 @@
# <small>nlohmann::json_sax::</small>json_sax
```cpp
// (1)
json_sax() = default;
// (2)
json_sax(const json_sax&) = default;
// (3)
json_sax(json_sax&&) noexcept = default;
```
1. Default constructor.
2. Copy constructor.
3. Move constructor.
`json_sax` is a pure abstract base class with no data members of its own, so all three constructors are
defaulted and only exist to make derived SAX consumers explicitly copyable/movable.
## Exception safety
No-throw guarantee: none of these constructors throw exceptions.
## Complexity
Constant.
<!-- NOLINT Examples -->
## Version history
- Added in version 3.2.0.
@@ -0,0 +1,30 @@
# <small>nlohmann::json_sax::</small>number_float_t
```cpp
using number_float_t = typename BasicJsonType::number_float_t;
```
The type used by the [`number_float`](number_float.md) callback for JSON floating-point numbers,
forwarded from the `BasicJsonType` template parameter.
See [`basic_json::number_float_t`](../basic_json/number_float_t.md) for more information.
## Examples
??? example
The example shows the type `number_float_t` and its relation to `basic_json::number_float_t`.
```cpp
--8<-- "examples/json_sax__number_float_t.cpp"
```
Output:
```
--8<-- "examples/json_sax__number_float_t.output"
```
## Version history
- Added in version 3.2.0.
@@ -0,0 +1,30 @@
# <small>nlohmann::json_sax::</small>number_integer_t
```cpp
using number_integer_t = typename BasicJsonType::number_integer_t;
```
The type used by the [`number_integer`](number_integer.md) callback for JSON integer numbers, forwarded
from the `BasicJsonType` template parameter.
See [`basic_json::number_integer_t`](../basic_json/number_integer_t.md) for more information.
## Examples
??? example
The example shows the type `number_integer_t` and its relation to `basic_json::number_integer_t`.
```cpp
--8<-- "examples/json_sax__number_integer_t.cpp"
```
Output:
```
--8<-- "examples/json_sax__number_integer_t.output"
```
## Version history
- Added in version 3.2.0.
@@ -0,0 +1,30 @@
# <small>nlohmann::json_sax::</small>number_unsigned_t
```cpp
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
```
The type used by the [`number_unsigned`](number_unsigned.md) callback for JSON unsigned integer numbers,
forwarded from the `BasicJsonType` template parameter.
See [`basic_json::number_unsigned_t`](../basic_json/number_unsigned_t.md) for more information.
## Examples
??? example
The example shows the type `number_unsigned_t` and its relation to `basic_json::number_unsigned_t`.
```cpp
--8<-- "examples/json_sax__number_unsigned_t.cpp"
```
Output:
```
--8<-- "examples/json_sax__number_unsigned_t.output"
```
## Version history
- Added in version 3.2.0.
@@ -0,0 +1,29 @@
# <small>nlohmann::json_sax::</small>operator=
```cpp
// (1)
json_sax& operator=(const json_sax&) = default;
// (2)
json_sax& operator=(json_sax&&) noexcept = default;
```
1. Copy assignment operator.
2. Move assignment operator.
`json_sax` is a pure abstract base class with no data members of its own, so both assignment operators
are defaulted and only exist to make derived SAX consumers explicitly copy-/move-assignable.
## Exception safety
No-throw guarantee: neither operator throws exceptions.
## Complexity
Constant.
<!-- NOLINT Examples -->
## Version history
- Added in version 3.2.0.
+30
View File
@@ -0,0 +1,30 @@
# <small>nlohmann::json_sax::</small>string_t
```cpp
using string_t = typename BasicJsonType::string_t;
```
The type used by the [`string`](string.md) and [`key`](key.md) callbacks for JSON strings and object
keys, forwarded from the `BasicJsonType` template parameter.
See [`basic_json::string_t`](../basic_json/string_t.md) for more information.
## Examples
??? example
The example shows the type `string_t` and its relation to `basic_json::string_t`.
```cpp
--8<-- "examples/json_sax__string_t.cpp"
```
Output:
```
--8<-- "examples/json_sax__string_t.output"
```
## Version history
- Added in version 3.2.0.
@@ -0,0 +1,22 @@
# <small>nlohmann::json_sax::</small>~json_sax
```cpp
virtual ~json_sax() = default;
```
Destructor. Virtual to allow proper destruction of derived SAX consumer classes through a
pointer/reference to `json_sax`.
## Exception safety
No-throw guarantee: this destructor never throws exceptions.
## Complexity
Constant.
<!-- NOLINT Examples -->
## Version history
- Added in version 3.2.0.
+4 -4
View File
@@ -8,16 +8,16 @@ This type preserves the insertion order of object keys.
## Iterator invalidation ## Iterator invalidation
The type is based on [`ordered_map`](ordered_map.md) which in turn uses a `std::vector` to store object elements. The type is based on [`ordered_map`](ordered_map/index.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 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 [`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. reference after the insertion point will point to the same index, which is now a different value.
## Complexity ## Complexity
[`ordered_map`](ordered_map.md) has no lookup index: every key-based object operation is a linear scan, so building or [`ordered_map`](ordered_map/index.md) has no lookup index: every key-based object operation is a linear scan, so building or
parsing an object of `n` keys costs O(n²) rather than O(n log n). See parsing an object of `n` keys costs O(n²) rather than O(n log n). See
[`ordered_map` complexity](ordered_map.md#complexity) for the per-operation table and for measured numbers. [`ordered_map` complexity](ordered_map/index.md#complexity) for the per-operation table and for measured numbers.
## Examples ## Examples
@@ -37,7 +37,7 @@ parsing an object of `n` keys costs O(n²) rather than O(n log n). See
## See also ## See also
- [ordered_map](ordered_map.md) - [ordered_map](ordered_map/index.md)
- [Object Order](../features/object_order.md) - [Object Order](../features/object_order.md)
## Version history ## Version history
@@ -0,0 +1,28 @@
# <small>nlohmann::ordered_map::</small>Container
```cpp
using Container = std::vector<std::pair<const Key, T>, Allocator>;
```
The base container type that `ordered_map` publicly inherits from. Elements are stored in insertion
order as `#!cpp std::pair<const Key, T>` entries in a `std::vector`.
## Examples
??? example
The example shows the type `Container`.
```cpp
--8<-- "examples/ordered_map__Container.cpp"
```
Output:
```
--8<-- "examples/ordered_map__Container.output"
```
## Version history
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
+61
View File
@@ -0,0 +1,61 @@
# <small>nlohmann::ordered_map::</small>at
```cpp
// (1)
T& at(const key_type& key);
const T& at(const key_type& key) const;
// (2)
template<class KeyType>
T& at(KeyType&& key);
template<class KeyType>
const T& at(KeyType&& key) const;
```
1. Returns a reference to the value mapped to `key`.
2. Same as (1), but for any `KeyType` comparable to `key_type` via [`key_compare`](key_compare.md)
(heterogeneous lookup, e.g. looking up by a `#!cpp const char*` without constructing a temporary
`key_type`). Only participates in overload resolution if `KeyType` is usable as a key type.
## Template parameters
`KeyType`
: a type comparable to `key_type` via [`key_compare`](key_compare.md)
## Parameters
`key` (in)
: key of the element to find
## Return value
reference to the mapped value of the element with key equal to `key`
## Exceptions
Throws `std::out_of_range` if no element with key `key` exists.
## Complexity
Linear in the number of elements.
## Examples
??? example
The example shows how `at` is used.
```cpp
--8<-- "examples/ordered_map__at.cpp"
```
Output:
```
--8<-- "examples/ordered_map__at.output"
```
## Version history
- Added in version 3.9.1 to implement [`nlohmann::ordered_json`](../ordered_json.md).
- Overload (2) added in version 3.11.0.
+53
View File
@@ -0,0 +1,53 @@
# <small>nlohmann::ordered_map::</small>count
```cpp
// (1)
size_type count(const key_type& key) const;
// (2)
template<class KeyType>
size_type count(KeyType&& key) const;
```
1. Returns the number of elements with key equal to `key` (0 or 1, since keys are unique).
2. Same as (1), but for any `KeyType` comparable to `key_type` via [`key_compare`](key_compare.md)
(heterogeneous lookup). Only participates in overload resolution if `KeyType` is usable as a key type.
## Template parameters
`KeyType`
: a type comparable to `key_type` via [`key_compare`](key_compare.md)
## Parameters
`key` (in)
: key of the elements to count
## Return value
number of elements with key equal to `key` (0 or 1)
## Complexity
Linear in the number of elements.
## Examples
??? example
The example shows how `count` is used.
```cpp
--8<-- "examples/ordered_map__count.cpp"
```
Output:
```
--8<-- "examples/ordered_map__count.output"
```
## Version history
- Added in version 3.9.1 to implement [`nlohmann::ordered_json`](../ordered_json.md).
- Overload (2) added in version 3.11.0.
@@ -0,0 +1,58 @@
# <small>nlohmann::ordered_map::</small>emplace
```cpp
// (1)
std::pair<iterator, bool> emplace(const key_type& key, T&& t);
// (2)
template<class KeyType>
std::pair<iterator, bool> emplace(KeyType&& key, T&& t);
```
1. Inserts `#!cpp {key, t}` if no element with an equal key already exists (per [`key_compare`](key_compare.md)),
appending it at the end to preserve insertion order. If an equal key already exists, does nothing.
2. Same as (1), but for any `KeyType` comparable to `key_type` via [`key_compare`](key_compare.md)
(heterogeneous lookup). Only participates in overload resolution if `KeyType` is usable as a key type.
## Template parameters
`KeyType`
: a type comparable to `key_type` via [`key_compare`](key_compare.md)
## Parameters
`key` (in)
: key of the element to insert
`t` (in)
: value of the element to insert
## Return value
pair of an iterator to the (possibly newly inserted) element, and a `bool` that is `true` if insertion
took place and `false` if an element with an equal key already existed
## Complexity
Linear in the number of elements.
## Examples
??? example
The example shows how `emplace` is used.
```cpp
--8<-- "examples/ordered_map__emplace.cpp"
```
Output:
```
--8<-- "examples/ordered_map__emplace.output"
```
## Version history
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
- Overload (2) added in version 3.11.0.
+75
View File
@@ -0,0 +1,75 @@
# <small>nlohmann::ordered_map::</small>erase
```cpp
// (1)
size_type erase(const key_type& key);
// (2)
template<class KeyType>
size_type erase(KeyType&& key);
// (3)
iterator erase(iterator pos);
// (4)
iterator erase(iterator first, iterator last);
```
1. Removes the element with key equal to `key`, if any, preserving the relative order of the remaining
elements.
2. Same as (1), but for any `KeyType` comparable to `key_type` via [`key_compare`](key_compare.md)
(heterogeneous lookup). Only participates in overload resolution if `KeyType` is usable as a key type.
3. Removes the element at `pos`.
4. Removes the elements in range `[first, last)`.
## Template parameters
`KeyType`
: a type comparable to `key_type` via [`key_compare`](key_compare.md)
## Parameters
`key` (in)
: key of the element to remove
`pos` (in)
: iterator to the element to remove
`first` (in)
: iterator to the first element to remove
`last` (in)
: iterator one past the last element to remove
## Return value
1. number of elements removed (0 or 1)
2. number of elements removed (0 or 1)
3. iterator following the removed element
4. iterator following the last removed element
## Complexity
Linear in the number of elements (elements after the removed one(s) are shifted to keep storage
contiguous).
## Examples
??? example
The example shows how `erase` is used.
```cpp
--8<-- "examples/ordered_map__erase.cpp"
```
Output:
```
--8<-- "examples/ordered_map__erase.output"
```
## Version history
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
- Overload (2) added in version 3.11.0.
+56
View File
@@ -0,0 +1,56 @@
# <small>nlohmann::ordered_map::</small>find
```cpp
// (1)
iterator find(const key_type& key);
const_iterator find(const key_type& key) const;
// (2)
template<class KeyType>
iterator find(KeyType&& key);
template<class KeyType>
const_iterator find(KeyType&& key) const;
```
1. Returns an iterator to the element with key equal to `key`, or `end()` if no such element exists.
2. Same as (1), but for any `KeyType` comparable to `key_type` via [`key_compare`](key_compare.md)
(heterogeneous lookup). Only participates in overload resolution if `KeyType` is usable as a key type.
## Template parameters
`KeyType`
: a type comparable to `key_type` via [`key_compare`](key_compare.md)
## Parameters
`key` (in)
: key of the element to find
## Return value
iterator to the element with key equal to `key`, or `end()` if not found
## Complexity
Linear in the number of elements.
## Examples
??? example
The example shows how `find` is used.
```cpp
--8<-- "examples/ordered_map__find.cpp"
```
Output:
```
--8<-- "examples/ordered_map__find.output"
```
## Version history
- Added in version 3.9.1 to implement [`nlohmann::ordered_json`](../ordered_json.md).
- Overload (2) added in version 3.11.0.
@@ -6,7 +6,7 @@ template<class Key, class T, class IgnoredLess = std::less<Key>,
struct ordered_map : std::vector<std::pair<const Key, T>, Allocator>; struct ordered_map : std::vector<std::pair<const Key, T>, Allocator>;
``` ```
A minimal map-like container that preserves insertion order for use within [`nlohmann::ordered_json`](ordered_json.md) A minimal map-like container that preserves insertion order for use within [`nlohmann::ordered_json`](../ordered_json.md)
(`nlohmann::basic_json<ordered_map>`). (`nlohmann::basic_json<ordered_map>`).
## Template parameters ## Template parameters
@@ -32,12 +32,12 @@ case all iterators (including the `end()` iterator) and all references to the el
- **key_type** - key type (`Key`) - **key_type** - key type (`Key`)
- **mapped_type** - mapped type (`T`) - **mapped_type** - mapped type (`T`)
- **Container** - base container type (`#!cpp std::vector<std::pair<const Key, T>, Allocator>`) - [**Container**](Container.md) - base container type (`#!cpp std::vector<std::pair<const Key, T>, Allocator>`)
- **iterator** - **iterator**
- **const_iterator** - **const_iterator**
- **size_type** - **size_type**
- **value_type** - **value_type**
- **key_compare** - key comparison function - [**key_compare**](key_compare.md) - key comparison function
```cpp ```cpp
std::equal_to<Key> // until C++14 std::equal_to<Key> // until C++14
@@ -46,15 +46,16 @@ std::equal_to<> // since C++14
## Member functions ## Member functions
- (constructor) - [(constructor)](ordered_map.md)
- (destructor) - [(destructor)](~ordered_map.md)
- **emplace** - [**operator=**](operator=.md)
- **operator\[\]** - [**emplace**](emplace.md)
- **at** - [**operator\[\]**](operator[].md)
- **erase** - [**at**](at.md)
- **count** - [**erase**](erase.md)
- **find** - [**count**](count.md)
- **insert** - [**find**](find.md)
- [**insert**](insert.md)
## Complexity ## Complexity
@@ -116,9 +117,9 @@ This differs from `#!cpp std::map`, where the same operations are O(log n).
## See also ## See also
- [ordered_json](ordered_json.md) - [ordered_json](../ordered_json.md)
## Version history ## Version history
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](ordered_json.md). - Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
- Added **key_compare** member in version 3.11.0. - Added **key_compare** member in version 3.11.0.
@@ -0,0 +1,63 @@
# <small>nlohmann::ordered_map::</small>insert
```cpp
// (1)
std::pair<iterator, bool> insert(value_type&& value);
std::pair<iterator, bool> insert(const value_type& value);
// (2)
template<typename InputIt>
void insert(InputIt first, InputIt last);
```
1. Inserts `value` if no element with an equal key already exists (per [`key_compare`](key_compare.md)),
appending it at the end to preserve insertion order. If an equal key already exists, does nothing.
2. Inserts the elements from range `[first, last)`, in iteration order, applying the same equal-key rule
as (1) to each element.
## Template parameters
`InputIt`
: an input iterator type
## Parameters
`value` (in)
: value to insert
`first` (in)
: iterator to the first element to insert
`last` (in)
: iterator one past the last element to insert
## Return value
1. pair of an iterator to the (possibly newly inserted) element, and a `bool` that is `true` if insertion
took place and `false` if an element with an equal key already existed
2. (none)
## Complexity
1. Linear in the number of elements.
2. Linear in the distance between `first` and `last`, times linear in the number of elements.
## Examples
??? example
The example shows how `insert` is used.
```cpp
--8<-- "examples/ordered_map__insert.cpp"
```
Output:
```
--8<-- "examples/ordered_map__insert.output"
```
## Version history
- Added in version 3.9.1 to implement [`nlohmann::ordered_json`](../ordered_json.md).
@@ -0,0 +1,34 @@
# <small>nlohmann::ordered_map::</small>key_compare
```cpp
using key_compare = std::equal_to<Key>; // until C++14
using key_compare = std::equal_to<>; // since C++14
```
The comparator used to determine key equality when looking up elements. Unlike `std::map`, `ordered_map`
uses linear search with `key_compare` rather than an ordering relation, since element order reflects
insertion order rather than key order.
Since C++14, the transparent `#!cpp std::equal_to<>` is used, which enables heterogeneous lookup (e.g.
looking up by a `#!cpp const char*` key without constructing a temporary `Key`).
## Examples
??? example
The example shows how `key_compare` is used.
```cpp
--8<-- "examples/ordered_map__key_compare.cpp"
```
Output:
```
--8<-- "examples/ordered_map__key_compare.output"
```
## Version history
- Added in version 3.11.0.
@@ -0,0 +1,32 @@
# <small>nlohmann::ordered_map::</small>operator=
```cpp
// (1)
ordered_map& operator=(const ordered_map& other);
// (2)
ordered_map& operator=(ordered_map&& other) noexcept(std::is_nothrow_move_assignable<Container>::value);
```
1. Copy assignment operator.
2. Move assignment operator.
## Parameters
`other` (in)
: value to assign from
## Return value
`*this`
## Complexity
1. Linear in the size of `other`.
2. Constant.
<!-- NOLINT Examples -->
## Version history
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
@@ -0,0 +1,62 @@
# <small>nlohmann::ordered_map::</small>operator[]
```cpp
// (1)
T& operator[](const key_type& key);
const T& operator[](const key_type& key) const;
// (2)
template<class KeyType>
T& operator[](KeyType&& key);
template<class KeyType>
const T& operator[](KeyType&& key) const;
```
1. Returns a reference to the value mapped to `key`, inserting a default-constructed `T` (non-`const`
overload only) if no such element exists yet.
2. Same as (1), but for any `KeyType` comparable to `key_type` via [`key_compare`](key_compare.md)
(heterogeneous lookup). Only participates in overload resolution if `KeyType` is usable as a key type.
## Template parameters
`KeyType`
: a type comparable to `key_type` via [`key_compare`](key_compare.md)
## Parameters
`key` (in)
: key of the element to find or insert
## Return value
reference to the mapped value of the element with key equal to `key`
## Exceptions
The `const` overloads throw `std::out_of_range` if no element with key `key` exists (they delegate to
[`at`](at.md)).
## Complexity
Linear in the number of elements.
## Examples
??? example
The example shows how `operator[]` is used.
```cpp
--8<-- "examples/ordered_map__operator_idx.cpp"
```
Output:
```
--8<-- "examples/ordered_map__operator_idx.output"
```
## Version history
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
- Overload (2) added in version 3.11.0.
@@ -0,0 +1,66 @@
# <small>nlohmann::ordered_map::</small>ordered_map
```cpp
// (1)
ordered_map() noexcept(noexcept(Container()));
// (2)
explicit ordered_map(const Allocator& alloc) noexcept(noexcept(Container(alloc)));
// (3)
template <class It>
ordered_map(It first, It last, const Allocator& alloc = Allocator());
// (4)
ordered_map(std::initializer_list<value_type> init, const Allocator& alloc = Allocator());
// (5)
ordered_map(const ordered_map&) = default;
// (6)
ordered_map(ordered_map&&) noexcept(std::is_nothrow_move_constructible<Container>::value) = default;
```
1. Default constructor. Creates an empty `ordered_map`.
2. Creates an empty `ordered_map` using the given allocator.
3. Creates an `ordered_map` from the elements in range `[first, last)`, inserted in iteration order.
4. Creates an `ordered_map` from an initializer list of key/value pairs, inserted in list order.
5. Copy constructor.
6. Move constructor.
These constructors are declared explicitly (rather than inherited via `#!cpp using Container::Container`)
because older compilers (GCC <= 5.5, Xcode <= 9.4) do not handle the inherited constructors correctly.
## Template parameters
`It`
: an input iterator type
## Parameters
`alloc` (in)
: allocator to use for the underlying container
`first` (in)
: iterator to the first element to insert
`last` (in)
: iterator one past the last element to insert
`init` (in)
: initializer list of key/value pairs to insert
## Complexity
1. Constant.
2. Constant.
3. Linear in the distance between `first` and `last`.
4. Linear in the size of `init`.
5. Linear in the size of `other`.
6. Constant.
<!-- NOLINT Examples -->
## Version history
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
@@ -0,0 +1,17 @@
# <small>nlohmann::ordered_map::</small>~ordered_map
```cpp
~ordered_map() = default;
```
Destroys the `ordered_map` and frees all allocated memory.
## Complexity
Linear in the number of elements.
<!-- NOLINT Examples -->
## Version history
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
@@ -0,0 +1,21 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
// an empty binary value is encoded differently by the two drafts:
// draft2 omits the optimized type marker for an empty byte array,
// while draft3 always writes it
json j = json::binary({});
// encode using BJData draft2 (the default)
auto v_draft2 = json::to_bjdata(j, true, true, json::bjdata_version_t::draft2);
// encode using BJData draft3
auto v_draft3 = json::to_bjdata(j, true, true, json::bjdata_version_t::draft3);
std::cout << "draft2 size: " << v_draft2.size() << '\n'
<< "draft3 size: " << v_draft3.size() << std::endl;
}
@@ -0,0 +1,2 @@
draft2 size: 4
draft3 size: 6
@@ -0,0 +1,11 @@
#include <iostream>
#include <nlohmann/json.hpp>
using byte_container_with_subtype = nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>>;
int main()
{
std::cout << std::boolalpha
<< std::is_same<byte_container_with_subtype::container_type, std::vector<std::uint8_t>>::value
<< std::endl;
}
@@ -0,0 +1,15 @@
#include <iostream>
#include <nlohmann/json.hpp>
using byte_container_with_subtype = nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>>;
int main()
{
byte_container_with_subtype c1({0xca, 0xfe});
byte_container_with_subtype c2({0xca, 0xfe});
byte_container_with_subtype c3({0xca, 0xfe}, 42);
std::cout << std::boolalpha
<< "c1 == c2: " << (c1 == c2) << '\n'
<< "c1 == c3: " << (c1 == c3) << std::endl;
}
@@ -0,0 +1,2 @@
c1 == c2: true
c1 == c3: false
@@ -0,0 +1,15 @@
#include <iostream>
#include <nlohmann/json.hpp>
using byte_container_with_subtype = nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>>;
int main()
{
byte_container_with_subtype c1({0xca, 0xfe});
byte_container_with_subtype c2({0xca, 0xfe});
byte_container_with_subtype c3({0xca, 0xfe}, 42);
std::cout << std::boolalpha
<< "c1 != c2: " << (c1 != c2) << '\n'
<< "c1 != c3: " << (c1 != c3) << std::endl;
}
@@ -0,0 +1,2 @@
c1 != c2: false
c1 != c3: true
@@ -0,0 +1,10 @@
#include <iostream>
#include <nlohmann/json.hpp>
using byte_container_with_subtype = nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>>;
int main()
{
std::cout << std::boolalpha
<< std::is_same<byte_container_with_subtype::subtype_type, std::uint64_t>::value << std::endl;
}
@@ -0,0 +1,13 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
// an initializer_list_t is what a braced-init-list of JSON values is deduced as
json::initializer_list_t init = {"a", 1, 2.0, false};
json j(init);
std::cout << j.dump() << std::endl;
}
@@ -0,0 +1 @@
["a",1,2.0,false]
@@ -0,0 +1,10 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
std::cout << std::boolalpha
<< std::is_same<json::json_sax_t::binary_t, json::binary_t>::value << std::endl;
}
@@ -0,0 +1 @@
true
@@ -0,0 +1,10 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
std::cout << std::boolalpha
<< std::is_same<json::json_sax_t::number_float_t, json::number_float_t>::value << std::endl;
}
@@ -0,0 +1 @@
true
@@ -0,0 +1,10 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
std::cout << std::boolalpha
<< std::is_same<json::json_sax_t::number_integer_t, json::number_integer_t>::value << std::endl;
}
@@ -0,0 +1 @@
true
@@ -0,0 +1,10 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
std::cout << std::boolalpha
<< std::is_same<json::json_sax_t::number_unsigned_t, json::number_unsigned_t>::value << std::endl;
}
@@ -0,0 +1 @@
true
@@ -0,0 +1,10 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
std::cout << std::boolalpha
<< std::is_same<json::json_sax_t::string_t, json::string_t>::value << std::endl;
}
@@ -0,0 +1 @@
true
+10
View File
@@ -0,0 +1,10 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
std::cout << std::boolalpha
<< std::is_same<json::json_sax_t, nlohmann::json_sax<json>>::value << std::endl;
}
@@ -0,0 +1 @@
true
@@ -0,0 +1,12 @@
#include <iostream>
#include <nlohmann/json.hpp>
int main()
{
using Map = nlohmann::ordered_map<std::string, int>;
std::cout << std::boolalpha
<< "Container is std::vector<std::pair<const Key, T>>: "
<< std::is_same<Map::Container, std::vector<std::pair<const std::string, int>>>::value
<< std::endl;
}
@@ -0,0 +1 @@
Container is std::vector<std::pair<const Key, T>>: true
@@ -0,0 +1,26 @@
#include <iostream>
#include <nlohmann/json.hpp>
int main()
{
nlohmann::ordered_map<std::string, int> m;
m["one"] = 1;
m["two"] = 2;
// access an existing element
std::cout << "m.at(\"one\") = " << m.at("one") << std::endl;
// modify through the reference returned by at()
m.at("two") = 22;
std::cout << "m.at(\"two\") = " << m.at("two") << std::endl;
// accessing a missing key throws
try
{
m.at("three");
}
catch (const std::out_of_range& e)
{
std::cout << "exception: " << e.what() << std::endl;
}
}
@@ -0,0 +1,3 @@
m.at("one") = 1
m.at("two") = 22
exception: key not found
@@ -0,0 +1,12 @@
#include <iostream>
#include <nlohmann/json.hpp>
int main()
{
nlohmann::ordered_map<std::string, int> m;
m["one"] = 1;
std::cout << std::boolalpha
<< "m.count(\"one\") = " << m.count("one") << '\n'
<< "m.count(\"two\") = " << m.count("two") << std::endl;
}
@@ -0,0 +1,2 @@
m.count("one") = 1
m.count("two") = 0
@@ -0,0 +1,15 @@
#include <iostream>
#include <nlohmann/json.hpp>
int main()
{
nlohmann::ordered_map<std::string, std::string> m;
// emplace a new element
auto res1 = m.emplace("one", "eins");
std::cout << std::boolalpha << "inserted: " << res1.second << ", value: " << res1.first->second << std::endl;
// emplace with an already-existing key: no-op, returns the existing element
auto res2 = m.emplace("one", "uno");
std::cout << std::boolalpha << "inserted: " << res2.second << ", value: " << res2.first->second << std::endl;
}
@@ -0,0 +1,2 @@
inserted: true, value: eins
inserted: false, value: eins
@@ -0,0 +1,24 @@
#include <iostream>
#include <nlohmann/json.hpp>
int main()
{
nlohmann::ordered_map<std::string, int> m;
m["one"] = 1;
m["two"] = 2;
m["three"] = 3;
// erase by key
std::size_t removed = m.erase("two");
std::cout << "removed by key: " << removed << std::endl;
// erase by iterator
m.erase(m.begin());
std::cout << "remaining: ";
for (const auto& element : m)
{
std::cout << element.first << ' ';
}
std::cout << std::endl;
}
@@ -0,0 +1,2 @@
removed by key: 1
remaining: three
@@ -0,0 +1,19 @@
#include <iostream>
#include <nlohmann/json.hpp>
int main()
{
nlohmann::ordered_map<std::string, int> m;
m["one"] = 1;
auto it = m.find("one");
if (it != m.end())
{
std::cout << "found: " << it->first << " = " << it->second << std::endl;
}
if (m.find("two") == m.end())
{
std::cout << "\"two\" not found" << std::endl;
}
}
@@ -0,0 +1,2 @@
found: one = 1
"two" not found
@@ -0,0 +1,21 @@
#include <iostream>
#include <nlohmann/json.hpp>
int main()
{
nlohmann::ordered_map<std::string, int> m;
// insert a single value
auto res = m.insert({"one", 1});
std::cout << std::boolalpha << "inserted: " << res.second << std::endl;
// insert a range from another container
std::vector<std::pair<const std::string, int>> more = {{"two", 2}, {"three", 3}};
m.insert(more.begin(), more.end());
for (const auto& element : m)
{
std::cout << element.first << ':' << element.second << ' ';
}
std::cout << std::endl;
}
@@ -0,0 +1,2 @@
inserted: true
one:1 two:2 three:3
@@ -0,0 +1,12 @@
#include <iostream>
#include <nlohmann/json.hpp>
int main()
{
using Map = nlohmann::ordered_map<std::string, int>;
Map::key_compare compare{};
std::cout << std::boolalpha
<< "compare(\"a\", \"a\") = " << compare("a", "a") << '\n'
<< "compare(\"a\", \"b\") = " << compare("a", "b") << std::endl;
}
@@ -0,0 +1,2 @@
compare("a", "a") = true
compare("a", "b") = false
@@ -0,0 +1,14 @@
#include <iostream>
#include <nlohmann/json.hpp>
int main()
{
nlohmann::ordered_map<std::string, int> m;
// operator[] inserts a default-constructed value if the key doesn't exist yet
m["one"] = 1;
std::cout << "m[\"one\"] = " << m["one"] << std::endl;
// accessing again just returns the existing value
std::cout << "m[\"one\"] = " << m["one"] << std::endl;
}
@@ -0,0 +1,2 @@
m["one"] = 1
m["one"] = 1
+7 -31
View File
@@ -16,15 +16,14 @@ before including the `json.hpp` header.
## Function with runtime assertions ## Function with runtime assertions
### Unchecked access to a const value ### Unchecked object access to a const value
Function [`operator[]`](../api/basic_json/operator%5B%5D.md) implements unchecked access for arrays and objects. Whereas Function [`operator[]`](../api/basic_json/operator%5B%5D.md) implements unchecked access for objects. Whereas a missing
a missing element is added in the case of non-const values, accessing a const value with a missing object key or an key is added in the case of non-const objects, accessing a const object with a missing key is undefined behavior (think
invalid array index is undefined behavior (think of a dereferenced null pointer) and yields a runtime assertion. This of a dereferenced null pointer) and yields a runtime assertion.
also applies to a [JSON pointer](json_pointer.md) that refers to a missing key or an invalid index.
If you are not sure whether an element exists, use checked access with the [`at` function](../api/basic_json/at.md) If you are not sure whether an element in an object exists, use checked access with the
or call the [`contains` function](../api/basic_json/contains.md) before. [`at` function](../api/basic_json/at.md) or call the [`contains` function](../api/basic_json/contains.md) before.
See also the documentation on [element access](element_access/index.md). See also the documentation on [element access](element_access/index.md).
@@ -47,30 +46,7 @@ See also the documentation on [element access](element_access/index.md).
Output: Output:
``` ```
Assertion failed: (it != m_data.m_value.object->end()), function operator[], file json.hpp, line 28795. Assertion failed: (m_value.object->find(key) != m_value.object->end()), function operator[], file json.hpp, line 2144.
```
??? example "Example 2: Invalid array index in a JSON pointer"
The following code will trigger an assertion at runtime:
```cpp
#include <nlohmann/json.hpp>
using json = nlohmann::json;
using namespace nlohmann::literals;
int main()
{
const json j = {{"array", {1, 2, 3}}};
auto v = j["/array/5"_json_pointer];
}
```
Output:
```
Assertion failed: (idx < m_data.m_value.array->size()), function operator[], file json.hpp, line 28758.
``` ```
### Constructing from an uninitialized iterator range ### Constructing from an uninitialized iterator range
@@ -174,7 +174,20 @@ The library maps CBOR types to JSON value types as follows:
!!! warning "Object keys" !!! warning "Object keys"
CBOR allows map keys of any type, whereas JSON only allows strings as keys in object values. Therefore, CBOR maps with keys other than UTF-8 strings are rejected. CBOR allows map keys of any type, whereas JSON only allows strings as keys in object values. Therefore, CBOR maps
with keys other than text strings (major type 3) are rejected with a
[`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) exception (or, with `allow_exceptions` set
to `false`, a discarded value) naming the type of the key that was found, for instance:
```
[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR object key: only string keys are supported, but found an unsigned integer; last byte: 0x01
```
This applies to the [SAX interface](../parsing/sax_interface.md) as well, as the key is read before it is passed
on. This is a deliberate restriction of the library's JSON value model, not an oversight: formats built on CBOR
maps with integer keys, such as COSE ([RFC 9052](https://www.rfc-editor.org/rfc/rfc9052.html)) or CWT
([RFC 8392](https://www.rfc-editor.org/rfc/rfc8392.html)), cannot be read with this library and need a
general-purpose CBOR library instead.
!!! warning "UTF-8 validation of text strings" !!! warning "UTF-8 validation of text strings"
@@ -138,6 +138,21 @@ The library maps MessagePack types to JSON value types as follows:
Any MessagePack output created by `to_msgpack` can be successfully parsed by `from_msgpack`. Any MessagePack output created by `to_msgpack` can be successfully parsed by `from_msgpack`.
!!! warning "Object keys"
MessagePack allows map keys of any type, whereas JSON only allows strings as keys in object values. Like the
JSON-compatible [profile](https://github.com/msgpack/msgpack/blob/master/spec.md#profile) sketched in the
MessagePack specification, this library restricts map keys to `str` values. Maps with keys of any other type are
rejected with a [`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) exception (or, with
`allow_exceptions` set to `false`, a discarded value) naming the type of the key that was found, for instance:
```
[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing MessagePack object key: only string keys are supported, but found nil; last byte: 0xC0
```
This applies to the [SAX interface](../parsing/sax_interface.md) as well, as the key is read before it is passed
on. Such input needs a general-purpose MessagePack library instead.
!!! warning "UTF-8 validation of string values" !!! warning "UTF-8 validation of string values"
The MessagePack specification requires `str` values (`fixstr`, `str 8`, `str 16`, `str 32`) to be valid UTF-8. The MessagePack specification requires `str` values (`fixstr`, `str 8`, `str 16`, `str 32`) to be valid UTF-8.
+3 -3
View File
@@ -51,16 +51,16 @@ If you do want to preserve the **insertion order**, you can use the type [`nlohm
--8<-- "examples/ordered_json.output" --8<-- "examples/ordered_json.output"
``` ```
Alternatively, [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) also preserves the insertion order and, unlike [`ordered_map`](../api/ordered_map.md), keeps a lookup index, so it does not have the quadratic cost described below. It is used through a small adapter ([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)). Alternatively, [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) also preserves the insertion order and, unlike [`ordered_map`](../api/ordered_map/index.md), keeps a lookup index, so it does not have the quadratic cost described below. It is used through a small adapter ([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)).
If the order does not matter and you only want faster lookup, `boost::unordered_flat_map`, `absl::flat_hash_map`, `absl::node_hash_map`, and several other hash maps work through an adapter that restores the template argument order `basic_json` expects; see [Template Parameter Requirements](types/template_parameters.md#objecttype). Note these are *unordered*, not insertion-ordered. If the order does not matter and you only want faster lookup, `boost::unordered_flat_map`, `absl::flat_hash_map`, `absl::node_hash_map`, and several other hash maps work through an adapter that restores the template argument order `basic_json` expects; see [Template Parameter Requirements](types/template_parameters.md#objecttype). Note these are *unordered*, not insertion-ordered.
[`tsl::ordered_map`](https://github.com/Tessil/ordered-map) cannot be used: its iterators expose the mapped value as `const`, while `basic_json` needs to modify it in place. [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) cannot be used: its iterators expose the mapped value as `const`, while `basic_json` needs to modify it in place.
The [`ordered_map`](../api/ordered_map.md) behind `nlohmann::ordered_json` is deliberately minimal and has no lookup The [`ordered_map`](../api/ordered_map/index.md) behind `nlohmann::ordered_json` is deliberately minimal and has no lookup
index, so every key access is a linear scan and building an object of `n` keys costs O(n²). This is unnoticeable at index, so every key access is a linear scan and building an object of `n` keys costs O(n²). This is unnoticeable at
typical object sizes but becomes significant for objects with many thousands of keys; see typical object sizes but becomes significant for objects with many thousands of keys; see
[`ordered_map` complexity](../api/ordered_map.md#complexity). The alternatives above keep a lookup index and do not [`ordered_map` complexity](../api/ordered_map/index.md#complexity). The alternatives above keep a lookup index and do not
have this cost. have this cost.
### Notes on parsing ### Notes on parsing
@@ -37,7 +37,7 @@ Requirements are split into two groups:
| Template parameter | Default | Notable substitutes | | Template parameter | Default | Notable substitutes |
|-------------------------------------------------------------------|-----------------------------------|-----------------------------------------------------------------------| |-------------------------------------------------------------------|-----------------------------------|-----------------------------------------------------------------------|
| [`ObjectType`](#objecttype) | `std::map` | [`nlohmann::ordered_map`](../../api/ordered_map.md), Abseil hash maps | | [`ObjectType`](#objecttype) | `std::map` | [`nlohmann::ordered_map`](../../api/ordered_map/index.md), Abseil hash maps |
| [`ArrayType`](#arraytype) | `std::vector` | `#!cpp std::deque` | | [`ArrayType`](#arraytype) | `std::vector` | `#!cpp std::deque` |
| [`StringType`](#stringtype) | `std::string` | `std::string`-like types over `char` | | [`StringType`](#stringtype) | `std::string` | `std::string`-like types over `char` |
| [`BooleanType`](#booleantype) | `bool` | none worth using | | [`BooleanType`](#booleantype) | `bool` | none worth using |
@@ -230,7 +230,7 @@ The library does not sort or de-duplicate keys itself; the behavior described in
| Container | Notes | | Container | Notes |
|----------------------------------------------------------------------------------|-------------------------------------------------------------------------------| |----------------------------------------------------------------------------------|-------------------------------------------------------------------------------|
| `#!cpp std::map` (default) | | | `#!cpp std::map` (default) | |
| [`nlohmann::ordered_map`](../../api/ordered_map.md) | used by [`ordered_json`](../../api/ordered_json.md); keeps insertion order | | [`nlohmann::ordered_map`](../../api/ordered_map/index.md) | used by [`ordered_json`](../../api/ordered_json.md); keeps insertion order |
| [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) | keeps insertion order; adapter puts `fifo_map_compare` in the comparator slot | | [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) | keeps insertion order; adapter puts `fifo_map_compare` in the comparator slot |
| `boost::container::map`, `boost::container::flat_map` | no adapter needed | | `boost::container::map`, `boost::container::flat_map` | no adapter needed |
| `#!cpp std::unordered_map` | through the adapter above; not with libstdc++ 9, see the note | | `#!cpp std::unordered_map` | through the adapter above; not with libstdc++ 9, see the note |
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -50,7 +50,7 @@ The public headers are in [`include/nlohmann`](https://github.com/nlohmann/json/
- [`adl_serializer.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/adl_serializer.hpp), [`byte_container_with_subtype.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/byte_container_with_subtype.hpp), and [`ordered_map.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/ordered_map.hpp) define - [`adl_serializer.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/adl_serializer.hpp), [`byte_container_with_subtype.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/byte_container_with_subtype.hpp), and [`ordered_map.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/ordered_map.hpp) define
[`adl_serializer`](../api/adl_serializer/index.md), [`adl_serializer`](../api/adl_serializer/index.md),
[`byte_container_with_subtype`](../api/byte_container_with_subtype/index.md), and [`byte_container_with_subtype`](../api/byte_container_with_subtype/index.md), and
[`ordered_map`](../api/ordered_map.md). [`ordered_map`](../api/ordered_map/index.md).
Everything else lives in [`detail/`](https://github.com/nlohmann/json/tree/develop/include/nlohmann/detail) and namespace `nlohmann::detail`, which is not part of the public API. Paths Everything else lives in [`detail/`](https://github.com/nlohmann/json/tree/develop/include/nlohmann/detail) and namespace `nlohmann::detail`, which is not part of the public API. Paths
below are relative to `include/nlohmann`. below are relative to `include/nlohmann`.
@@ -97,7 +97,7 @@ is generated from these files with `make amalgamate` and must not be edited by h
The library provides two specializations: The library provides two specializations:
- [`json`](../api/json.md) uses all default template arguments. - [`json`](../api/json.md) uses all default template arguments.
- [`ordered_json`](../api/ordered_json.md) uses [`ordered_map`](../api/ordered_map.md) as `ObjectType` to keep the - [`ordered_json`](../api/ordered_json.md) uses [`ordered_map`](../api/ordered_map/index.md) as `ObjectType` to keep the
insertion order of object keys. insertion order of object keys.
The requirements on the template arguments are listed in The requirements on the template arguments are listed in
+9 -2
View File
@@ -343,13 +343,20 @@ A string could not be read from a [binary format](../features/binary_formats/ind
string was read where one was required (for instance as a map key), the string's length specification is invalid, or string was read where one was required (for instance as a map key), the string's length specification is invalid, or
the string's bytes are not valid UTF-8. the string's bytes are not valid UTF-8.
CBOR and MessagePack allow map keys of any type, but JSON object keys are always strings. Maps with keys of any other
type (for instance integers or `null`) are therefore not supported; see the notes on
[CBOR](../features/binary_formats/cbor.md) and [MessagePack](../features/binary_formats/messagepack.md).
!!! failure "Example messages" !!! failure "Example messages"
``` ```
[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0xFF [json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR object key: only string keys are supported, but found an unsigned integer; last byte: 0x01
``` ```
``` ```
[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing MessagePack string: expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0xFF [json.exception.parse_error.113] parse error at byte 2: syntax error while parsing MessagePack object key: only string keys are supported, but found nil; last byte: 0xC0
```
```
[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x7C
``` ```
``` ```
[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON char: byte after 'C' must be in range 0x00..0x7F; last byte: 0x82 [json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON char: byte after 'C' must be in range 0x00..0x7F; last byte: 0x82
+2 -1
View File
@@ -2,7 +2,8 @@
This page summarizes the notable changes of every release and links to the relevant documentation. This page summarizes the notable changes of every release and links to the relevant documentation.
The **complete release notes** — including all changes, the download files, and their checksums — are The **complete release notes** — including all changes, the download files, and their checksums — are
published on the [GitHub releases page](https://github.com/nlohmann/json/releases). published on the [GitHub releases page](https://github.com/nlohmann/json/releases). For a raw,
signature-level diff of the public API between releases, see [API Changes](api_changes.md).
## v3.12.0 (2025-04-11) ## v3.12.0 (2025-04-11)
+30 -1
View File
@@ -52,6 +52,7 @@ nav:
- "FAQ": home/faq.md - "FAQ": home/faq.md
- home/exceptions.md - home/exceptions.md
- home/releases.md - home/releases.md
- home/api_changes.md
- home/design_goals.md - home/design_goals.md
- home/architecture.md - home/architecture.md
- home/customers.md - home/customers.md
@@ -119,6 +120,7 @@ nav:
- 'begin': api/basic_json/begin.md - 'begin': api/basic_json/begin.md
- 'binary': api/basic_json/binary.md - 'binary': api/basic_json/binary.md
- 'binary_t': api/basic_json/binary_t.md - 'binary_t': api/basic_json/binary_t.md
- 'bjdata_version_t': api/basic_json/bjdata_version_t.md
- 'boolean_t': api/basic_json/boolean_t.md - 'boolean_t': api/basic_json/boolean_t.md
- 'cbegin': api/basic_json/cbegin.md - 'cbegin': api/basic_json/cbegin.md
- 'cbor_tag_handler_t': api/basic_json/cbor_tag_handler_t.md - 'cbor_tag_handler_t': api/basic_json/cbor_tag_handler_t.md
@@ -157,6 +159,7 @@ nav:
- 'get_to': api/basic_json/get_to.md - 'get_to': api/basic_json/get_to.md
- 'std::formatter&lt;basic_json&gt;': api/basic_json/std_formatter.md - 'std::formatter&lt;basic_json&gt;': api/basic_json/std_formatter.md
- 'std::hash&lt;basic_json&gt;': api/basic_json/std_hash.md - 'std::hash&lt;basic_json&gt;': api/basic_json/std_hash.md
- 'initializer_list_t': api/basic_json/initializer_list_t.md
- 'input_format_t': api/basic_json/input_format_t.md - 'input_format_t': api/basic_json/input_format_t.md
- 'insert': api/basic_json/insert.md - 'insert': api/basic_json/insert.md
- 'invalid_iterator': api/basic_json/invalid_iterator.md - 'invalid_iterator': api/basic_json/invalid_iterator.md
@@ -175,6 +178,7 @@ nav:
- 'is_structured': api/basic_json/is_structured.md - 'is_structured': api/basic_json/is_structured.md
- 'items': api/basic_json/items.md - 'items': api/basic_json/items.md
- 'json_base_class_t': api/basic_json/json_base_class_t.md - 'json_base_class_t': api/basic_json/json_base_class_t.md
- 'json_sax_t': api/basic_json/json_sax_t.md
- 'json_serializer': api/basic_json/json_serializer.md - 'json_serializer': api/basic_json/json_serializer.md
- 'max_size': api/basic_json/max_size.md - 'max_size': api/basic_json/max_size.md
- 'meta': api/basic_json/meta.md - 'meta': api/basic_json/meta.md
@@ -232,9 +236,13 @@ nav:
- 'Overview': api/byte_container_with_subtype/index.md - 'Overview': api/byte_container_with_subtype/index.md
- '(constructor)': api/byte_container_with_subtype/byte_container_with_subtype.md - '(constructor)': api/byte_container_with_subtype/byte_container_with_subtype.md
- 'clear_subtype': api/byte_container_with_subtype/clear_subtype.md - 'clear_subtype': api/byte_container_with_subtype/clear_subtype.md
- 'container_type': api/byte_container_with_subtype/container_type.md
- 'has_subtype': api/byte_container_with_subtype/has_subtype.md - 'has_subtype': api/byte_container_with_subtype/has_subtype.md
- 'operator==': api/byte_container_with_subtype/operator_eq.md
- 'operator!=': api/byte_container_with_subtype/operator_ne.md
- 'set_subtype': api/byte_container_with_subtype/set_subtype.md - 'set_subtype': api/byte_container_with_subtype/set_subtype.md
- 'subtype': api/byte_container_with_subtype/subtype.md - 'subtype': api/byte_container_with_subtype/subtype.md
- 'subtype_type': api/byte_container_with_subtype/subtype_type.md
- adl_serializer: - adl_serializer:
- 'Overview': api/adl_serializer/index.md - 'Overview': api/adl_serializer/index.md
- 'from_json': api/adl_serializer/from_json.md - 'from_json': api/adl_serializer/from_json.md
@@ -260,25 +268,46 @@ nav:
- 'to_string': api/json_pointer/to_string.md - 'to_string': api/json_pointer/to_string.md
- json_sax: - json_sax:
- 'Overview': api/json_sax/index.md - 'Overview': api/json_sax/index.md
- '(Constructor)': api/json_sax/json_sax.md
- '(Destructor)': api/json_sax/~json_sax.md
- 'operator=': api/json_sax/operator=.md
- 'binary': api/json_sax/binary.md - 'binary': api/json_sax/binary.md
- 'binary_t': api/json_sax/binary_t.md
- 'boolean': api/json_sax/boolean.md - 'boolean': api/json_sax/boolean.md
- 'end_array': api/json_sax/end_array.md - 'end_array': api/json_sax/end_array.md
- 'end_object': api/json_sax/end_object.md - 'end_object': api/json_sax/end_object.md
- 'key': api/json_sax/key.md - 'key': api/json_sax/key.md
- 'null': api/json_sax/null.md - 'null': api/json_sax/null.md
- 'number_float': api/json_sax/number_float.md - 'number_float': api/json_sax/number_float.md
- 'number_float_t': api/json_sax/number_float_t.md
- 'number_integer': api/json_sax/number_integer.md - 'number_integer': api/json_sax/number_integer.md
- 'number_integer_t': api/json_sax/number_integer_t.md
- 'number_unsigned': api/json_sax/number_unsigned.md - 'number_unsigned': api/json_sax/number_unsigned.md
- 'number_unsigned_t': api/json_sax/number_unsigned_t.md
- 'parse_error': api/json_sax/parse_error.md - 'parse_error': api/json_sax/parse_error.md
- 'start_array': api/json_sax/start_array.md - 'start_array': api/json_sax/start_array.md
- 'start_object': api/json_sax/start_object.md - 'start_object': api/json_sax/start_object.md
- 'string': api/json_sax/string.md - 'string': api/json_sax/string.md
- 'string_t': api/json_sax/string_t.md
- 'operator<<(basic_json), operator<<(json_pointer)': api/operator_ltlt.md - 'operator<<(basic_json), operator<<(json_pointer)': api/operator_ltlt.md
- 'operator>>(basic_json)': api/operator_gtgt.md - 'operator>>(basic_json)': api/operator_gtgt.md
- 'operator""_json': api/operator_literal_json.md - 'operator""_json': api/operator_literal_json.md
- 'operator""_json_pointer': api/operator_literal_json_pointer.md - 'operator""_json_pointer': api/operator_literal_json_pointer.md
- 'ordered_json': api/ordered_json.md - 'ordered_json': api/ordered_json.md
- 'ordered_map': api/ordered_map.md - ordered_map:
- 'Overview': api/ordered_map/index.md
- '(Constructor)': api/ordered_map/ordered_map.md
- '(Destructor)': api/ordered_map/~ordered_map.md
- 'operator=': api/ordered_map/operator=.md
- 'at': api/ordered_map/at.md
- 'Container': api/ordered_map/Container.md
- 'count': api/ordered_map/count.md
- 'emplace': api/ordered_map/emplace.md
- 'erase': api/ordered_map/erase.md
- 'find': api/ordered_map/find.md
- 'insert': api/ordered_map/insert.md
- 'key_compare': api/ordered_map/key_compare.md
- 'operator[]': api/ordered_map/operator[].md
- macros: - macros:
- 'Overview': api/macros/index.md - 'Overview': api/macros/index.md
- 'JSON_ASSERT': api/macros/json_assert.md - 'JSON_ASSERT': api/macros/json_assert.md
@@ -22,7 +22,9 @@ template<typename BinaryType>
class byte_container_with_subtype : public BinaryType class byte_container_with_subtype : public BinaryType
{ {
public: public:
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/container_type/
using container_type = BinaryType; using container_type = BinaryType;
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/subtype_type/
using subtype_type = std::uint64_t; using subtype_type = std::uint64_t;
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
@@ -54,12 +56,14 @@ class byte_container_with_subtype : public BinaryType
, m_has_subtype(true) , m_has_subtype(true)
{} {}
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/operator_eq/
bool operator==(const byte_container_with_subtype& rhs) const bool operator==(const byte_container_with_subtype& rhs) const
{ {
return std::tie(static_cast<const BinaryType&>(*this), m_subtype, m_has_subtype) == return std::tie(static_cast<const BinaryType&>(*this), m_subtype, m_has_subtype) ==
std::tie(static_cast<const BinaryType&>(rhs), rhs.m_subtype, rhs.m_has_subtype); std::tie(static_cast<const BinaryType&>(rhs), rhs.m_subtype, rhs.m_has_subtype);
} }
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/operator_ne/
bool operator!=(const byte_container_with_subtype& rhs) const bool operator!=(const byte_container_with_subtype& rhs) const
{ {
return !(rhs == *this); return !(rhs == *this);
+168 -2
View File
@@ -1324,6 +1324,80 @@ class binary_reader
} }
} }
/*!
@brief reads a CBOR object key
RFC 8949 allows any data item as a map key, but only strings have a
counterpart in JSON. A key of any other type is rejected with a message
naming that type, rather than the one @ref get_cbor_string gives for a
malformed string.
@param[out] result created key
@return whether key creation completed
*/
bool get_cbor_object_key(string_t& result)
{
// EOF and major type 3 (text string) are left to get_cbor_string
if (current == char_traits<char_type>::eof() || (static_cast<unsigned int>(current) & 0xE0u) == 0x60u)
{
return get_cbor_string(result);
}
const char* found = nullptr;
switch (static_cast<unsigned int>(current) >> 5u)
{
case 0:
found = "an unsigned integer";
break;
case 1:
found = "a negative integer";
break;
case 2:
found = "a byte string";
break;
case 4:
found = "an array";
break;
case 5:
found = "a map";
break;
case 6:
found = "a tag";
break;
default: // major type 7
switch (current)
{
case 0xF4:
case 0xF5:
found = "a boolean";
break;
case 0xF6:
found = "null";
break;
case 0xF7:
found = "undefined";
break;
case 0xF9:
case 0xFA:
case 0xFB:
found = "a floating-point number";
break;
case 0xFF:
found = "a break stop code";
break;
default:
found = "a simple value";
break;
}
break;
}
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(input_format_t::cbor, concat("only string keys are supported, but found ", found, "; last byte: 0x", last_token), "object key"), nullptr));
}
/*! /*!
@brief reads a definite-length CBOR byte array @brief reads a definite-length CBOR byte array
@@ -1568,7 +1642,7 @@ class binary_reader
if (top.is_object) if (top.is_object)
{ {
key.clear(); key.clear();
if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) if (JSON_HEDLEY_UNLIKELY(!get_cbor_object_key(key) || !sax->key(key)))
{ {
return false; return false;
} }
@@ -2069,6 +2143,98 @@ class binary_reader
} }
} }
/*!
@brief reads a MessagePack object key
The MessagePack specification allows any type as a map key, but only
strings have a counterpart in JSON. A key of any other type is rejected
with a message naming that type, rather than the one @ref
get_msgpack_string gives for a malformed string.
@param[out] result created key
@return whether key creation completed
*/
bool get_msgpack_object_key(string_t& result)
{
const char* found = nullptr;
switch (current)
{
case 0xC0:
found = "nil";
break;
case 0xC2:
case 0xC3:
found = "a boolean";
break;
case 0xCA:
case 0xCB:
found = "a float";
break;
case 0xC4:
case 0xC5:
case 0xC6:
found = "a bin";
break;
case 0xC7:
case 0xC8:
case 0xC9:
case 0xD4:
case 0xD5:
case 0xD6:
case 0xD7:
case 0xD8:
found = "an ext";
break;
case 0xCC:
case 0xCD:
case 0xCE:
case 0xCF:
case 0xD0:
case 0xD1:
case 0xD2:
case 0xD3:
found = "an integer";
break;
case 0xDC:
case 0xDD:
found = "an array";
break;
case 0xDE:
case 0xDF:
found = "a map";
break;
default:
// fixint, fixmap, and fixarray; strings, EOF, and the unused
// byte 0xC1 are left to get_msgpack_string
if (current == char_traits<char_type>::eof())
{
return get_msgpack_string(result);
}
if (current <= 0x7F || current >= 0xE0)
{
found = "an integer";
}
else if (current <= 0x8F)
{
found = "a map";
}
else if (current <= 0x9F)
{
found = "an array";
}
else
{
return get_msgpack_string(result);
}
break;
}
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(input_format_t::msgpack, concat("only string keys are supported, but found ", found, "; last byte: 0x", last_token), "object key"), nullptr));
}
/*! /*!
@brief reads a MessagePack byte array @brief reads a MessagePack byte array
@@ -2231,7 +2397,7 @@ class binary_reader
{ {
get(); get();
key.clear(); key.clear();
if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key))) if (JSON_HEDLEY_UNLIKELY(!get_msgpack_object_key(key) || !sax->key(key)))
{ {
return false; return false;
} }
@@ -33,15 +33,21 @@ input.
template<typename BasicJsonType> template<typename BasicJsonType>
struct json_sax struct json_sax
{ {
/// @sa https://json.nlohmann.me/api/json_sax/number_integer_t/
using number_integer_t = typename BasicJsonType::number_integer_t; using number_integer_t = typename BasicJsonType::number_integer_t;
/// @sa https://json.nlohmann.me/api/json_sax/number_unsigned_t/
using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
/// @sa https://json.nlohmann.me/api/json_sax/number_float_t/
using number_float_t = typename BasicJsonType::number_float_t; using number_float_t = typename BasicJsonType::number_float_t;
/// @sa https://json.nlohmann.me/api/json_sax/string_t/
using string_t = typename BasicJsonType::string_t; using string_t = typename BasicJsonType::string_t;
/// @sa https://json.nlohmann.me/api/json_sax/binary_t/
using binary_t = typename BasicJsonType::binary_t; using binary_t = typename BasicJsonType::binary_t;
/*! /*!
@brief a null value was read @brief a null value was read
@return whether parsing should proceed @return whether parsing should proceed
@sa https://json.nlohmann.me/api/json_sax/null/
*/ */
virtual bool null() = 0; virtual bool null() = 0;
@@ -49,6 +55,7 @@ struct json_sax
@brief a boolean value was read @brief a boolean value was read
@param[in] val boolean value @param[in] val boolean value
@return whether parsing should proceed @return whether parsing should proceed
@sa https://json.nlohmann.me/api/json_sax/boolean/
*/ */
virtual bool boolean(bool val) = 0; virtual bool boolean(bool val) = 0;
@@ -56,6 +63,7 @@ struct json_sax
@brief an integer number was read @brief an integer number was read
@param[in] val integer value @param[in] val integer value
@return whether parsing should proceed @return whether parsing should proceed
@sa https://json.nlohmann.me/api/json_sax/number_integer/
*/ */
virtual bool number_integer(number_integer_t val) = 0; virtual bool number_integer(number_integer_t val) = 0;
@@ -63,6 +71,7 @@ struct json_sax
@brief an unsigned integer number was read @brief an unsigned integer number was read
@param[in] val unsigned integer value @param[in] val unsigned integer value
@return whether parsing should proceed @return whether parsing should proceed
@sa https://json.nlohmann.me/api/json_sax/number_unsigned/
*/ */
virtual bool number_unsigned(number_unsigned_t val) = 0; virtual bool number_unsigned(number_unsigned_t val) = 0;
@@ -71,6 +80,7 @@ struct json_sax
@param[in] val floating-point value @param[in] val floating-point value
@param[in] s raw token value @param[in] s raw token value
@return whether parsing should proceed @return whether parsing should proceed
@sa https://json.nlohmann.me/api/json_sax/number_float/
*/ */
virtual bool number_float(number_float_t val, const string_t& s) = 0; virtual bool number_float(number_float_t val, const string_t& s) = 0;
@@ -79,6 +89,7 @@ struct json_sax
@param[in] val string value @param[in] val string value
@return whether parsing should proceed @return whether parsing should proceed
@note It is safe to move the passed string value. @note It is safe to move the passed string value.
@sa https://json.nlohmann.me/api/json_sax/string/
*/ */
virtual bool string(string_t& val) = 0; virtual bool string(string_t& val) = 0;
@@ -87,6 +98,7 @@ struct json_sax
@param[in] val binary value @param[in] val binary value
@return whether parsing should proceed @return whether parsing should proceed
@note It is safe to move the passed binary value. @note It is safe to move the passed binary value.
@sa https://json.nlohmann.me/api/json_sax/binary/
*/ */
virtual bool binary(binary_t& val) = 0; virtual bool binary(binary_t& val) = 0;
@@ -95,6 +107,7 @@ struct json_sax
@param[in] elements number of object elements or -1 if unknown @param[in] elements number of object elements or -1 if unknown
@return whether parsing should proceed @return whether parsing should proceed
@note binary formats may report the number of elements @note binary formats may report the number of elements
@sa https://json.nlohmann.me/api/json_sax/start_object/
*/ */
virtual bool start_object(std::size_t elements) = 0; virtual bool start_object(std::size_t elements) = 0;
@@ -103,12 +116,14 @@ struct json_sax
@param[in] val object key @param[in] val object key
@return whether parsing should proceed @return whether parsing should proceed
@note It is safe to move the passed string. @note It is safe to move the passed string.
@sa https://json.nlohmann.me/api/json_sax/key/
*/ */
virtual bool key(string_t& val) = 0; virtual bool key(string_t& val) = 0;
/*! /*!
@brief the end of an object was read @brief the end of an object was read
@return whether parsing should proceed @return whether parsing should proceed
@sa https://json.nlohmann.me/api/json_sax/end_object/
*/ */
virtual bool end_object() = 0; virtual bool end_object() = 0;
@@ -117,12 +132,14 @@ struct json_sax
@param[in] elements number of array elements or -1 if unknown @param[in] elements number of array elements or -1 if unknown
@return whether parsing should proceed @return whether parsing should proceed
@note binary formats may report the number of elements @note binary formats may report the number of elements
@sa https://json.nlohmann.me/api/json_sax/start_array/
*/ */
virtual bool start_array(std::size_t elements) = 0; virtual bool start_array(std::size_t elements) = 0;
/*! /*!
@brief the end of an array was read @brief the end of an array was read
@return whether parsing should proceed @return whether parsing should proceed
@sa https://json.nlohmann.me/api/json_sax/end_array/
*/ */
virtual bool end_array() = 0; virtual bool end_array() = 0;
@@ -132,16 +149,23 @@ struct json_sax
@param[in] last_token the last read token @param[in] last_token the last read token
@param[in] ex an exception object describing the error @param[in] ex an exception object describing the error
@return whether parsing should proceed (must return false) @return whether parsing should proceed (must return false)
@sa https://json.nlohmann.me/api/json_sax/parse_error/
*/ */
virtual bool parse_error(std::size_t position, virtual bool parse_error(std::size_t position,
const std::string& last_token, const std::string& last_token,
const detail::exception& ex) = 0; const detail::exception& ex) = 0;
/// @sa https://json.nlohmann.me/api/json_sax/json_sax/
json_sax() = default; json_sax() = default;
/// @sa https://json.nlohmann.me/api/json_sax/json_sax/
json_sax(const json_sax&) = default; json_sax(const json_sax&) = default;
/// @sa https://json.nlohmann.me/api/json_sax/json_sax/
json_sax(json_sax&&) noexcept = default; json_sax(json_sax&&) noexcept = default;
/// @sa https://json.nlohmann.me/api/json_sax/operator=/
json_sax& operator=(const json_sax&) = default; json_sax& operator=(const json_sax&) = default;
/// @sa https://json.nlohmann.me/api/json_sax/operator=/
json_sax& operator=(json_sax&&) noexcept = default; json_sax& operator=(json_sax&&) noexcept = default;
/// @sa https://json.nlohmann.me/api/json_sax/~json_sax/
virtual ~json_sax() = default; virtual ~json_sax() = default;
}; };
+76 -39
View File
@@ -206,7 +206,6 @@ class lexer : public lexer_base<BasicJsonType>
explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false, bool discard_number_values_ = false) noexcept explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false, bool discard_number_values_ = false) noexcept
: ia(std::move(adapter)) : ia(std::move(adapter))
, ignore_comments(ignore_comments_) , ignore_comments(ignore_comments_)
, decimal_point_char(static_cast<char_int_type>(get_decimal_point()))
, discard_number_values(discard_number_values_) , discard_number_values(discard_number_values_)
{} {}
@@ -222,8 +221,7 @@ class lexer : public lexer_base<BasicJsonType>
// locales // locales
///////////////////// /////////////////////
/// return the locale-dependent decimal point /// return the decimal point of the current locale
JSON_HEDLEY_PURE
static char get_decimal_point() noexcept static char get_decimal_point() noexcept
{ {
const auto* loc = localeconv(); const auto* loc = localeconv();
@@ -1092,9 +1090,10 @@ class lexer : public lexer_base<BasicJsonType>
token_type::value_float if number could be successfully scanned, token_type::value_float if number could be successfully scanned,
token_type::parse_error otherwise token_type::parse_error otherwise
@note The scanner is independent of the current locale. Internally, the @note The scanner is independent of the current locale: token_buffer
locale's decimal point is used instead of `.` to work with the always holds `.`. Only the std::strtod fallback of convert_number()
locale-dependent converters. depends on the locale, and it looks up the decimal point right
before converting (see convert_float_locale_aware()).
*/ */
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. 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.
{ {
@@ -1183,7 +1182,7 @@ scan_number_zero:
{ {
case '.': case '.':
{ {
add(decimal_point_char); add(current);
decimal_point_position = token_buffer.size() - 1; decimal_point_position = token_buffer.size() - 1;
goto scan_number_decimal1; goto scan_number_decimal1;
} }
@@ -1220,7 +1219,7 @@ scan_number_any1:
case '.': case '.':
{ {
add(decimal_point_char); add(current);
decimal_point_position = token_buffer.size() - 1; decimal_point_position = token_buffer.size() - 1;
goto scan_number_decimal1; goto scan_number_decimal1;
} }
@@ -1462,9 +1461,9 @@ scan_number_done:
// Only a number below 1 can carry further insignificant zeros, and only // Only a number below 1 can carry further insignificant zeros, and only
// while the count stays at the limit does removing them change the // while the count stays at the limit does removing them change the
// answer - so this loop is skipped for all but a few tokens. Note // answer - so this loop is skipped for all but a few tokens. The
// token_buffer holds the locale's decimal point, so the fraction is // fraction is located through decimal_point_position rather than by
// located through decimal_point_position rather than by searching '.'. // searching '.'.
if (lead_zero != 0) if (lead_zero != 0)
{ {
JSON_ASSERT(has_dot != 0); // an integer "0" cannot reach the limit JSON_ASSERT(has_dot != 0); // an integer "0" cannot reach the limit
@@ -1482,8 +1481,8 @@ scan_number_done:
@brief convert the number text in token_buffer to its value and token type @brief convert the number text in token_buffer to its value and token type
The digit sequence in token_buffer has already been validated (by the The digit sequence in token_buffer has already been validated (by the
scan_number() state machine or by the contiguous fast path) and holds the scan_number() state machine or by the contiguous fast path) and holds '.'
locale decimal point in place of '.'. Integers are parsed first and fall as decimal point, independent of the locale. Integers are parsed first and fall
back to floating point on overflow. This is shared so both scanners produce back to floating point on overflow. This is shared so both scanners produce
identical results. identical results.
@@ -1563,7 +1562,7 @@ scan_number_done:
// integer conversion above overflowed. Prefer std::from_chars // integer conversion above overflowed. Prefer std::from_chars
// (Eisel-Lemire, locale-independent, correctly rounded) when available; // (Eisel-Lemire, locale-independent, correctly rounded) when available;
// otherwise the exact Clinger fast path (double only); otherwise the // otherwise the exact Clinger fast path (double only); otherwise the
// locale-aware strtof/strtod. // locale-aware strtof/strtod/strtold.
if (parse_float_from_chars(num_begin, num_end, value_float)) if (parse_float_from_chars(num_begin, num_end, value_float))
{ {
return token_type::value_float; return token_type::value_float;
@@ -1572,26 +1571,75 @@ scan_number_done:
// extra pass over the token's bytes, which otherwise shows up on // extra pass over the token's bytes, which otherwise shows up on
// high-precision inputs such as canada.json // high-precision inputs such as canada.json
if (mantissa_fits_clinger(mantissa_end) if (mantissa_fits_clinger(mantissa_end)
&& parse_float_fast(num_begin, num_end, decimal_point_char, value_float)) && parse_float_fast(num_begin, num_end, value_float))
{ {
return token_type::value_float; return token_type::value_float;
} }
char* endptr = nullptr; // NOLINT(misc-const-correctness,cppcoreguidelines-pro-type-vararg,hicpp-vararg) convert_float_locale_aware();
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; return token_type::value_float;
} }
/*!
@brief convert the float in token_buffer with strtof/strtod/strtold
These functions expect the decimal point of the *current* locale, so it is
looked up right before the conversion instead of once when the lexer is
constructed: a locale change in between (by a parser callback, a SAX
handler, or another thread) must not truncate the value (#5198). The
token has been validated before, so if the conversion stops early and the
decimal point changed in the meantime, the locale changed between the
lookup and the call, and the conversion is repeated with the new decimal
point. If the decimal point did not change, a retry cannot succeed: the
locale's decimal point is not a single character (e.g., the two-byte
U+066B of ar_EG.UTF-8 or fa_IR.UTF-8) and cannot be substituted in place.
The value strtod parsed up to that point is kept, as before this change.
Note that changing the locale in another thread *while* strtod runs is
undefined behavior of the C library, which this function cannot prevent.
*/
void convert_float_locale_aware()
{
const bool has_dot = decimal_point_position != std::string::npos;
char decimal_point = get_decimal_point();
for (;;)
{
const bool substitute = has_dot && decimal_point != '.';
if (substitute)
{
token_buffer[decimal_point_position] = static_cast<typename string_t::value_type>(decimal_point);
}
char* endptr = nullptr; // NOLINT(misc-const-correctness,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
strtof(value_float, token_buffer.data(), &endptr);
if (substitute)
{
// get_string() hands the token to the SAX interface with '.'
token_buffer[decimal_point_position] = '.';
}
if (JSON_HEDLEY_LIKELY(endptr == token_buffer.data() + token_buffer.size()))
{
return;
}
// retry only if the locale changed; otherwise, this would loop forever
const char current_decimal_point = get_decimal_point();
if (current_decimal_point == decimal_point)
{
return;
}
decimal_point = current_decimal_point;
}
}
/*! /*!
@brief contiguous fast path for scanning a number @brief contiguous fast path for scanning a number
Parses the whole number token straight from the input buffer, avoiding the Parses the whole number token straight from the input buffer, avoiding the
per-character get()/add() of scan_number(). On success it fills token_buffer per-character get()/add() of scan_number(). On success it fills token_buffer
(with the locale decimal point substituted, as scan_number() does) and (as scan_number() does) and
returns the token type. On anything it does not fully recognize as a returns the token type. On anything it does not fully recognize as a
well-formed number it makes no state change and returns well-formed number it makes no state change and returns
token_type::uninitialized, so the caller falls back to scan_number(), which token_type::uninitialized, so the caller falls back to scan_number(), which
@@ -1707,16 +1755,11 @@ scan_number_done:
} }
#endif #endif
// materialize the token exactly as scan_number() would, substituting the // materialize the token exactly as scan_number() would. reset() already
// locale decimal point so convert_number()'s strtof fallback stays valid. // cleared token_buffer, so append() fills it (assign() is avoided
// reset() already cleared token_buffer, so append() fills it (assign() is // because custom string_t types need not provide it)
// avoided because custom string_t types need not provide it)
token_buffer.append(reinterpret_cast<const typename string_t::value_type*>(data), len); token_buffer.append(reinterpret_cast<const typename string_t::value_type*>(data), len);
if (dot_index != std::string::npos) decimal_point_position = dot_index;
{
token_buffer[dot_index] = static_cast<typename string_t::value_type>(decimal_point_char);
decimal_point_position = dot_index;
}
ia.bulk_skip(len - 1); ia.bulk_skip(len - 1);
position.chars_read_total += (len - 1); position.chars_read_total += (len - 1);
@@ -1983,11 +2026,7 @@ scan_number_done:
/// return current string value (implicitly resets the token; useful only once) /// return current string value (implicitly resets the token; useful only once)
string_t& get_string() string_t& get_string()
{ {
// translate decimal points from locale back to '.' (#4084) // a number token holds '.' regardless of the locale (#4084)
if (decimal_point_char != '.' && decimal_point_position != std::string::npos)
{
token_buffer[decimal_point_position] = '.';
}
return token_buffer; return token_buffer;
} }
@@ -2283,9 +2322,7 @@ scan_number_done:
number_unsigned_t value_unsigned = 0; number_unsigned_t value_unsigned = 0;
number_float_t value_float = 0; number_float_t value_float = 0;
/// the decimal point /// the position of the decimal point in token_buffer
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; std::size_t decimal_point_position = std::string::npos;
/// whether the caller (e.g. accept()/json_sax_acceptor) only needs the /// whether the caller (e.g. accept()/json_sax_acceptor) only needs the
+8 -13
View File
@@ -118,14 +118,12 @@ std::strtod. The parser only activates for number_float_t == double; float and
long double keep the std::strtof/std::strtold paths (see the templated overload long double keep the std::strtof/std::strtold paths (see the templated overload
below). below).
@param[in] first pointer to the first character of the number @param[in] first pointer to the first character of the number
@param[in] last pointer past the last character @param[in] last pointer past the last character
@param[in] decimal_point the (locale-dependent) decimal point character @param[out] out the parsed value on success
@param[out] out the parsed value on success
@return true if the value was parsed exactly; false to fall back to strtod @return true if the value was parsed exactly; false to fall back to strtod
*/ */
template<typename DecimalPointType> inline bool parse_float_fast(const char* first, const char* last, double& out) noexcept
bool parse_float_fast(const char* first, const char* last, DecimalPointType decimal_point, double& out) noexcept
{ {
#if defined(FLT_EVAL_METHOD) && FLT_EVAL_METHOD != 0 #if defined(FLT_EVAL_METHOD) && FLT_EVAL_METHOD != 0
// Clinger's fast path is only exact when double operations are evaluated in // Clinger's fast path is only exact when double operations are evaluated in
@@ -136,7 +134,6 @@ bool parse_float_fast(const char* first, const char* last, DecimalPointType deci
// std::from_chars / std::strtod path. // std::from_chars / std::strtod path.
static_cast<void>(first); static_cast<void>(first);
static_cast<void>(last); static_cast<void>(last);
static_cast<void>(decimal_point);
static_cast<void>(out); static_cast<void>(out);
return false; return false;
#else #else
@@ -175,7 +172,7 @@ bool parse_float_fast(const char* first, const char* last, DecimalPointType deci
++num_digits; ++num_digits;
fractional_digits += static_cast<int>(seen_dot); fractional_digits += static_cast<int>(seen_dot);
} }
else if (static_cast<DecimalPointType>(c) == decimal_point) else if (c == '.')
{ {
if (JSON_HEDLEY_UNLIKELY(seen_dot)) if (JSON_HEDLEY_UNLIKELY(seen_dot))
{ {
@@ -260,8 +257,8 @@ bool parse_float_fast(const char* first, const char* last, DecimalPointType deci
} }
/// fast float path is only exact for `double`; decline for float/long double /// fast float path is only exact for `double`; decline for float/long double
template<typename DecimalPointType, typename FloatType> template<typename FloatType>
bool parse_float_fast(const char* /*first*/, const char* /*last*/, DecimalPointType /*decimal_point*/, FloatType& /*out*/) noexcept bool parse_float_fast(const char* /*first*/, const char* /*last*/, FloatType& /*out*/) noexcept
{ {
return false; return false;
} }
@@ -273,9 +270,7 @@ std::from_chars is locale-independent, correctly rounded, and - via the
Eisel-Lemire algorithm in modern standard libraries - much faster than strtod Eisel-Lemire algorithm in modern standard libraries - much faster than strtod
over the whole value range (not just the Clinger subset). It is used only when over the whole value range (not just the Clinger subset). It is used only when
__cpp_lib_to_chars indicates full floating-point support and only when it __cpp_lib_to_chars indicates full floating-point support and only when it
consumes the entire token ([first, last)); a partial parse means the buffer consumes the entire token ([first, last)). An under-/overflow (result_out_of_range) also declines, so
uses a non-'.' locale decimal point, in which case the caller falls back to the
locale-aware path. An under-/overflow (result_out_of_range) also declines, so
the caller's strtod fallback supplies the well-defined ±inf/0 result the parser the caller's strtod fallback supplies the well-defined ±inf/0 result the parser
expects (side-stepping the P4168 divergence between implementations). expects (side-stepping the P4168 divergence between implementations).
+3 -8
View File
@@ -56,6 +56,7 @@ class json_pointer
public: public:
// for backwards compatibility accept BasicJsonType // for backwards compatibility accept BasicJsonType
/// @sa https://json.nlohmann.me/api/json_pointer/string_t/
using string_t = typename string_t_helper<RefStringType>::type; using string_t = typename string_t_helper<RefStringType>::type;
/// @brief create JSON pointer /// @brief create JSON pointer
@@ -535,10 +536,6 @@ class json_pointer
@return const reference to the JSON value pointed to by the JSON @return const reference to the JSON value pointed to by the JSON
pointer pointer
@pre Every object key and array index the pointer refers to exists.
Like the const operator[] for keys and indices, a missing one is
undefined behavior, guarded by a runtime assertion.
@throw parse_error.106 if an array index begins with '0' @throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number @throw parse_error.109 if an array index was not a number
@throw out_of_range.402 if the array index '-' is used @throw out_of_range.402 if the array index '-' is used
@@ -553,8 +550,7 @@ class json_pointer
{ {
case detail::value_t::object: case detail::value_t::object:
{ {
// use unchecked object access; the const operator[] // use unchecked object access
// asserts that the key exists
ptr = &ptr->operator[](reference_token); ptr = &ptr->operator[](reference_token);
break; break;
} }
@@ -567,8 +563,7 @@ class json_pointer
JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr)); JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr));
} }
// use unchecked array access; the const operator[] // use unchecked array access
// asserts that the index exists
ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token)); ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
break; break;
} }
+41 -2
View File
@@ -202,22 +202,31 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
using serializer = ::nlohmann::detail::serializer<basic_json>; using serializer = ::nlohmann::detail::serializer<basic_json>;
public: public:
/// @brief the type of the JSON value
/// @sa https://json.nlohmann.me/api/basic_json/value_t/
using value_t = detail::value_t; using value_t = detail::value_t;
/// JSON Pointer, see @ref nlohmann::json_pointer /// JSON Pointer, see @ref nlohmann::json_pointer
using json_pointer = ::nlohmann::json_pointer<StringType>; using json_pointer = ::nlohmann::json_pointer<StringType>;
template<typename T, typename SFINAE> template<typename T, typename SFINAE>
using json_serializer = JSONSerializer<T, SFINAE>; using json_serializer = JSONSerializer<T, SFINAE>;
/// how to treat decoding errors /// how to treat decoding errors
/// @sa https://json.nlohmann.me/api/basic_json/error_handler_t/
using error_handler_t = detail::error_handler_t; using error_handler_t = detail::error_handler_t;
/// how to treat CBOR tags /// how to treat CBOR tags
/// @sa https://json.nlohmann.me/api/basic_json/cbor_tag_handler_t/
using cbor_tag_handler_t = detail::cbor_tag_handler_t; using cbor_tag_handler_t = detail::cbor_tag_handler_t;
/// how to encode BJData /// how to encode BJData
/// @sa https://json.nlohmann.me/api/basic_json/bjdata_version_t/
using bjdata_version_t = detail::bjdata_version_t; using bjdata_version_t = detail::bjdata_version_t;
/// helper type for initializer lists of basic_json values /// helper type for initializer lists of basic_json values
/// @sa https://json.nlohmann.me/api/basic_json/initializer_list_t/
using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>; using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
/// @brief the type of the SAX interface used to parse and serialize the JSON value
/// @sa https://json.nlohmann.me/api/basic_json/input_format_t/
using input_format_t = detail::input_format_t; using input_format_t = detail::input_format_t;
/// SAX interface type, see @ref nlohmann::json_sax /// SAX interface type, see @ref nlohmann::json_sax
/// @sa https://json.nlohmann.me/api/basic_json/json_sax_t/
using json_sax_t = json_sax<basic_json>; using json_sax_t = json_sax<basic_json>;
//////////////// ////////////////
@@ -359,15 +368,19 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// the template arguments passed to class @ref basic_json. /// the template arguments passed to class @ref basic_json.
/// @{ /// @{
#if defined(JSON_HAS_CPP_14)
/// @brief default object key comparator type /// @brief default object key comparator type
/// The actual object key comparator type (@ref object_comparator_t) may be /// The actual object key comparator type (@ref object_comparator_t) may be
/// different. /// different.
/// @sa https://json.nlohmann.me/api/basic_json/default_object_comparator_t/ /// @sa https://json.nlohmann.me/api/basic_json/default_object_comparator_t/
#if defined(JSON_HAS_CPP_14)
// use of transparent comparator avoids unnecessary repeated construction of temporaries // use of transparent comparator avoids unnecessary repeated construction of temporaries
// in functions involving lookup by key with types other than object_t::key_type (aka. StringType) // in functions involving lookup by key with types other than object_t::key_type (aka. StringType)
using default_object_comparator_t = std::less<>; using default_object_comparator_t = std::less<>;
#else #else
/// @brief default object key comparator type
/// The actual object key comparator type (@ref object_comparator_t) may be
/// different.
/// @sa https://json.nlohmann.me/api/basic_json/default_object_comparator_t/
using default_object_comparator_t = std::less<StringType>; using default_object_comparator_t = std::less<StringType>;
#endif #endif
@@ -1913,6 +1926,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// other constructors and destructor // // other constructors and destructor //
/////////////////////////////////////// ///////////////////////////////////////
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
template<typename JsonRef, template<typename JsonRef,
detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>, detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>,
std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 > std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 >
@@ -2496,6 +2510,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
@throw what @ref json_serializer<ValueType> `from_json()` method throws if conversion is required @throw what @ref json_serializer<ValueType> `from_json()` method throws if conversion is required
@since version 2.1.0 @since version 2.1.0
@sa https://json.nlohmann.me/api/basic_json/get/
*/ */
template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>> template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>>
#if defined(JSON_HAS_CPP_14) #if defined(JSON_HAS_CPP_14)
@@ -2539,6 +2555,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
@sa see @ref get_ptr() for explicit pointer-member access @sa see @ref get_ptr() for explicit pointer-member access
@since version 1.0.0 @since version 1.0.0
@sa https://json.nlohmann.me/api/basic_json/get/
*/ */
template<typename PointerType, typename std::enable_if< template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value, int>::type = 0> std::is_pointer<PointerType>::value, int>::type = 0>
@@ -2565,6 +2583,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// specialization to allow calling get_to with a basic_json value // specialization to allow calling get_to with a basic_json value
// see https://github.com/nlohmann/json/issues/2175 // see https://github.com/nlohmann/json/issues/2175
/// @sa https://json.nlohmann.me/api/basic_json/get_to/
template<typename ValueType, template<typename ValueType,
detail::enable_if_t < detail::enable_if_t <
detail::is_basic_json<ValueType>::value, detail::is_basic_json<ValueType>::value,
@@ -2575,6 +2594,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return v; return v;
} }
/// @sa https://json.nlohmann.me/api/basic_json/get_to/
template < template <
typename T, std::size_t N, typename T, std::size_t N,
typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
@@ -2639,6 +2659,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
@since version 1.0.0 @since version 1.0.0
*/ */
/// @sa https://json.nlohmann.me/api/basic_json/operator_ValueType/
template < typename ValueType, typename std::enable_if < template < typename ValueType, typename std::enable_if <
detail::conjunction < detail::conjunction <
detail::negation<std::is_pointer<ValueType>>, detail::negation<std::is_pointer<ValueType>>,
@@ -2866,7 +2887,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// const operator[] only works for arrays // const operator[] only works for arrays
if (JSON_HEDLEY_LIKELY(is_array())) if (JSON_HEDLEY_LIKELY(is_array()))
{ {
JSON_ASSERT(idx < m_data.m_value.array->size());
return m_data.m_value.array->operator[](idx); return m_data.m_value.array->operator[](idx);
} }
@@ -2912,12 +2932,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// these two functions resolve a (const) char * ambiguity affecting Clang and MSVC // these two functions resolve a (const) char * ambiguity affecting Clang and MSVC
// (they seemingly cannot be constrained to resolve the ambiguity) // (they seemingly cannot be constrained to resolve the ambiguity)
/// @sa https://json.nlohmann.me/api/basic_json/operator[]/
template<typename T> template<typename T>
reference operator[](T* key) reference operator[](T* key)
{ {
return operator[](typename object_t::key_type(key)); return operator[](typename object_t::key_type(key));
} }
/// @sa https://json.nlohmann.me/api/basic_json/operator[]/
template<typename T> template<typename T>
const_reference operator[](T* key) const const_reference operator[](T* key) const
{ {
@@ -3127,6 +3149,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
} }
/// @sa https://json.nlohmann.me/api/basic_json/value/
template < class ValueType, class BasicJsonType, detail::enable_if_t < template < class ValueType, class BasicJsonType, detail::enable_if_t <
detail::is_basic_json<BasicJsonType>::value detail::is_basic_json<BasicJsonType>::value
&& detail::is_getable<basic_json_t, ValueType>::value && detail::is_getable<basic_json_t, ValueType>::value
@@ -3137,6 +3160,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return value(ptr.convert(), default_value); return value(ptr.convert(), default_value);
} }
/// @sa https://json.nlohmann.me/api/basic_json/value/
template < class ValueType, class BasicJsonType, class ReturnType = typename value_return_type<ValueType>::type, template < class ValueType, class BasicJsonType, class ReturnType = typename value_return_type<ValueType>::type,
detail::enable_if_t < detail::enable_if_t <
detail::is_basic_json<BasicJsonType>::value detail::is_basic_json<BasicJsonType>::value
@@ -3516,6 +3540,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return ptr.contains(this); return ptr.contains(this);
} }
/// @sa https://json.nlohmann.me/api/basic_json/contains/
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0> template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens) JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
@@ -4935,6 +4960,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return result; return result;
} }
/// @sa https://json.nlohmann.me/api/basic_json/parse/
JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len))
static basic_json parse(detail::span_input_adapter&& i, static basic_json parse(detail::span_input_adapter&& i,
@@ -4971,6 +4997,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true); return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true);
} }
/// @sa https://json.nlohmann.me/api/basic_json/accept/
JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len)) JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len))
static bool accept(detail::span_input_adapter&& i, static bool accept(detail::span_input_adapter&& i,
@@ -5354,6 +5381,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return result; return result;
} }
/// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
template<typename T> template<typename T>
JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
@@ -5365,6 +5393,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler); return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler);
} }
/// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
static basic_json from_cbor(detail::span_input_adapter&& i, static basic_json from_cbor(detail::span_input_adapter&& i,
@@ -5420,6 +5449,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return result; return result;
} }
/// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
template<typename T> template<typename T>
JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
@@ -5430,6 +5460,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return from_msgpack(ptr, ptr + len, strict, allow_exceptions); return from_msgpack(ptr, ptr + len, strict, allow_exceptions);
} }
/// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
static basic_json from_msgpack(detail::span_input_adapter&& i, static basic_json from_msgpack(detail::span_input_adapter&& i,
@@ -5484,6 +5515,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return result; return result;
} }
/// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
template<typename T> template<typename T>
JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
@@ -5494,6 +5526,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return from_ubjson(ptr, ptr + len, strict, allow_exceptions); return from_ubjson(ptr, ptr + len, strict, allow_exceptions);
} }
/// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
static basic_json from_ubjson(detail::span_input_adapter&& i, static basic_json from_ubjson(detail::span_input_adapter&& i,
@@ -5622,6 +5655,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return result; return result;
} }
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
template<typename T> template<typename T>
JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
@@ -5632,6 +5666,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return from_bson(ptr, ptr + len, strict, allow_exceptions); return from_bson(ptr, ptr + len, strict, allow_exceptions);
} }
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
static basic_json from_bson(detail::span_input_adapter&& i, static basic_json from_bson(detail::span_input_adapter&& i,
@@ -5664,6 +5699,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return ptr.get_unchecked(this); return ptr.get_unchecked(this);
} }
/// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0> template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens) JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr) reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr)
@@ -5678,6 +5714,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return ptr.get_unchecked(this); return ptr.get_unchecked(this);
} }
/// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0> template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens) JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
const_reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr) const const_reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr) const
@@ -5692,6 +5729,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return ptr.get_checked(this); return ptr.get_checked(this);
} }
/// @sa https://json.nlohmann.me/api/basic_json/at/
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0> template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens) JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr) reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr)
@@ -5706,6 +5744,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return ptr.get_checked(this); return ptr.get_checked(this);
} }
/// @sa https://json.nlohmann.me/api/basic_json/at/
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0> template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens) JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
const_reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr) const const_reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr) const
+35
View File
@@ -30,30 +30,41 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
{ {
using key_type = Key; using key_type = Key;
using mapped_type = T; using mapped_type = T;
/// @sa https://json.nlohmann.me/api/ordered_map/Container/
using Container = std::vector<std::pair<const Key, T>, Allocator>; using Container = std::vector<std::pair<const Key, T>, Allocator>;
using iterator = typename Container::iterator; using iterator = typename Container::iterator;
using const_iterator = typename Container::const_iterator; using const_iterator = typename Container::const_iterator;
using size_type = typename Container::size_type; using size_type = typename Container::size_type;
using value_type = typename Container::value_type; using value_type = typename Container::value_type;
#ifdef JSON_HAS_CPP_14 #ifdef JSON_HAS_CPP_14
/// @sa https://json.nlohmann.me/api/ordered_map/key_compare/
using key_compare = std::equal_to<>; using key_compare = std::equal_to<>;
#else #else
/// @sa https://json.nlohmann.me/api/ordered_map/key_compare/
using key_compare = std::equal_to<Key>; using key_compare = std::equal_to<Key>;
#endif #endif
// Explicit constructors instead of `using Container::Container` // Explicit constructors instead of `using Container::Container`
// otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4) // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4)
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
ordered_map() noexcept(noexcept(Container())) : Container{} {} ordered_map() noexcept(noexcept(Container())) : Container{} {}
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
explicit ordered_map(const Allocator& alloc) noexcept(noexcept(Container(alloc))) : Container{alloc} {} explicit ordered_map(const Allocator& alloc) noexcept(noexcept(Container(alloc))) : Container{alloc} {}
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
template <class It> template <class It>
ordered_map(It first, It last, const Allocator& alloc = Allocator()) ordered_map(It first, It last, const Allocator& alloc = Allocator())
: Container{first, last, alloc} {} : Container{first, last, alloc} {}
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
ordered_map(std::initializer_list<value_type> init, const Allocator& alloc = Allocator() ) ordered_map(std::initializer_list<value_type> init, const Allocator& alloc = Allocator() )
: Container{init, alloc} {} : Container{init, alloc} {}
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
ordered_map(const ordered_map&) = default; ordered_map(const ordered_map&) = default;
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
ordered_map(ordered_map&&) noexcept(std::is_nothrow_move_constructible<Container>::value) = default; ordered_map(ordered_map&&) noexcept(std::is_nothrow_move_constructible<Container>::value) = default;
/// @sa https://json.nlohmann.me/api/ordered_map/~ordered_map/
~ordered_map() = default; ~ordered_map() = default;
/// @sa https://json.nlohmann.me/api/ordered_map/operator=/
ordered_map& operator=(const ordered_map& other) ordered_map& operator=(const ordered_map& other)
{ {
if (this != &other) if (this != &other)
@@ -64,12 +75,14 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return *this; return *this;
} }
/// @sa https://json.nlohmann.me/api/ordered_map/operator=/
ordered_map& operator=(ordered_map&& other) noexcept(std::is_nothrow_move_assignable<Container>::value) ordered_map& operator=(ordered_map&& other) noexcept(std::is_nothrow_move_assignable<Container>::value)
{ {
Container::operator=(std::move(static_cast<Container&>(other))); Container::operator=(std::move(static_cast<Container&>(other)));
return *this; return *this;
} }
/// @sa https://json.nlohmann.me/api/ordered_map/emplace/
std::pair<iterator, bool> emplace(const key_type& key, T&& t) std::pair<iterator, bool> emplace(const key_type& key, T&& t)
{ {
for (auto it = this->begin(); it != this->end(); ++it) for (auto it = this->begin(); it != this->end(); ++it)
@@ -83,6 +96,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return {std::prev(this->end()), true}; return {std::prev(this->end()), true};
} }
/// @sa https://json.nlohmann.me/api/ordered_map/emplace/
template<class KeyType, detail::enable_if_t< template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0> detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
std::pair<iterator, bool> emplace(KeyType && key, T && t) std::pair<iterator, bool> emplace(KeyType && key, T && t)
@@ -98,11 +112,13 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return {std::prev(this->end()), true}; return {std::prev(this->end()), true};
} }
/// @sa https://json.nlohmann.me/api/ordered_map/operator[]/
T& operator[](const key_type& key) T& operator[](const key_type& key)
{ {
return emplace(key, T{}).first->second; return emplace(key, T{}).first->second;
} }
/// @sa https://json.nlohmann.me/api/ordered_map/operator[]/
template<class KeyType, detail::enable_if_t< template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0> detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
T & operator[](KeyType && key) T & operator[](KeyType && key)
@@ -110,11 +126,13 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return emplace(std::forward<KeyType>(key), T{}).first->second; return emplace(std::forward<KeyType>(key), T{}).first->second;
} }
/// @sa https://json.nlohmann.me/api/ordered_map/operator[]/
const T& operator[](const key_type& key) const const T& operator[](const key_type& key) const
{ {
return at(key); return at(key);
} }
/// @sa https://json.nlohmann.me/api/ordered_map/operator[]/
template<class KeyType, detail::enable_if_t< template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0> detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
const T & operator[](KeyType && key) const const T & operator[](KeyType && key) const
@@ -122,6 +140,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return at(std::forward<KeyType>(key)); return at(std::forward<KeyType>(key));
} }
/// @sa https://json.nlohmann.me/api/ordered_map/at/
T& at(const key_type& key) T& at(const key_type& key)
{ {
for (auto it = this->begin(); it != this->end(); ++it) for (auto it = this->begin(); it != this->end(); ++it)
@@ -135,6 +154,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
JSON_THROW(std::out_of_range("key not found")); JSON_THROW(std::out_of_range("key not found"));
} }
/// @sa https://json.nlohmann.me/api/ordered_map/at/
template<class KeyType, detail::enable_if_t< template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0> detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
T & at(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward) T & at(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
@@ -150,6 +170,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
JSON_THROW(std::out_of_range("key not found")); JSON_THROW(std::out_of_range("key not found"));
} }
/// @sa https://json.nlohmann.me/api/ordered_map/at/
const T& at(const key_type& key) const const T& at(const key_type& key) const
{ {
for (auto it = this->begin(); it != this->end(); ++it) for (auto it = this->begin(); it != this->end(); ++it)
@@ -163,6 +184,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
JSON_THROW(std::out_of_range("key not found")); JSON_THROW(std::out_of_range("key not found"));
} }
/// @sa https://json.nlohmann.me/api/ordered_map/at/
template<class KeyType, detail::enable_if_t< template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0> detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
const T & at(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward) const T & at(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
@@ -178,6 +200,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
JSON_THROW(std::out_of_range("key not found")); JSON_THROW(std::out_of_range("key not found"));
} }
/// @sa https://json.nlohmann.me/api/ordered_map/erase/
size_type erase(const key_type& key) size_type erase(const key_type& key)
{ {
for (auto it = this->begin(); it != this->end(); ++it) for (auto it = this->begin(); it != this->end(); ++it)
@@ -197,6 +220,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return 0; return 0;
} }
/// @sa https://json.nlohmann.me/api/ordered_map/erase/
template<class KeyType, detail::enable_if_t< template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0> detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
size_type erase(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward) size_type erase(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
@@ -218,11 +242,13 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return 0; return 0;
} }
/// @sa https://json.nlohmann.me/api/ordered_map/erase/
iterator erase(iterator pos) iterator erase(iterator pos)
{ {
return erase(pos, std::next(pos)); return erase(pos, std::next(pos));
} }
/// @sa https://json.nlohmann.me/api/ordered_map/erase/
iterator erase(iterator first, iterator last) iterator erase(iterator first, iterator last)
{ {
if (first == last) if (first == last)
@@ -276,6 +302,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return Container::begin() + offset; return Container::begin() + offset;
} }
/// @sa https://json.nlohmann.me/api/ordered_map/count/
size_type count(const key_type& key) const size_type count(const key_type& key) const
{ {
for (auto it = this->begin(); it != this->end(); ++it) for (auto it = this->begin(); it != this->end(); ++it)
@@ -288,6 +315,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return 0; return 0;
} }
/// @sa https://json.nlohmann.me/api/ordered_map/count/
template<class KeyType, detail::enable_if_t< template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0> detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
size_type count(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward) size_type count(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
@@ -302,6 +330,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return 0; return 0;
} }
/// @sa https://json.nlohmann.me/api/ordered_map/find/
iterator find(const key_type& key) iterator find(const key_type& key)
{ {
for (auto it = this->begin(); it != this->end(); ++it) for (auto it = this->begin(); it != this->end(); ++it)
@@ -314,6 +343,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return Container::end(); return Container::end();
} }
/// @sa https://json.nlohmann.me/api/ordered_map/find/
template<class KeyType, detail::enable_if_t< template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0> detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
iterator find(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward) iterator find(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
@@ -328,6 +358,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return Container::end(); return Container::end();
} }
/// @sa https://json.nlohmann.me/api/ordered_map/find/
const_iterator find(const key_type& key) const const_iterator find(const key_type& key) const
{ {
for (auto it = this->begin(); it != this->end(); ++it) for (auto it = this->begin(); it != this->end(); ++it)
@@ -340,6 +371,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return Container::end(); return Container::end();
} }
/// @sa https://json.nlohmann.me/api/ordered_map/find/
template<class KeyType, detail::enable_if_t< template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0> detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
const_iterator find(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward) const_iterator find(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
@@ -354,11 +386,13 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return Container::end(); return Container::end();
} }
/// @sa https://json.nlohmann.me/api/ordered_map/insert/
std::pair<iterator, bool> insert( value_type&& value ) std::pair<iterator, bool> insert( value_type&& value )
{ {
return emplace(value.first, std::move(value.second)); return emplace(value.first, std::move(value.second));
} }
/// @sa https://json.nlohmann.me/api/ordered_map/insert/
std::pair<iterator, bool> insert( const value_type& value ) std::pair<iterator, bool> insert( const value_type& value )
{ {
for (auto it = this->begin(); it != this->end(); ++it) for (auto it = this->begin(); it != this->end(); ++it)
@@ -376,6 +410,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
using require_input_iter = typename std::enable_if<std::is_convertible<typename std::iterator_traits<InputIt>::iterator_category, using require_input_iter = typename std::enable_if<std::is_convertible<typename std::iterator_traits<InputIt>::iterator_category,
std::input_iterator_tag>::value>::type; std::input_iterator_tag>::value>::type;
/// @sa https://json.nlohmann.me/api/ordered_map/insert/
template<typename InputIt, typename = require_input_iter<InputIt>> template<typename InputIt, typename = require_input_iter<InputIt>>
void insert(InputIt first, InputIt last) void insert(InputIt first, InputIt last)
{ {
File diff suppressed because it is too large Load Diff
+43 -2
View File
@@ -1830,10 +1830,51 @@ TEST_CASE("CBOR")
SECTION("invalid string in map") SECTION("invalid string in map")
{ {
json _; json _;
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0xa1, 0xff, 0x01})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0xFF", json::parse_error&); CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0xa1, 0xff, 0x01})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR object key: only string keys are supported, but found a break stop code; last byte: 0xFF", json::parse_error&);
CHECK(json::from_cbor(std::vector<uint8_t>({0xa1, 0xff, 0x01}), true, false).is_discarded()); CHECK(json::from_cbor(std::vector<uint8_t>({0xa1, 0xff, 0x01}), true, false).is_discarded());
} }
SECTION("non-string key (see #2766 and #3381)")
{
// only text strings map to JSON object keys; any other key is
// rejected with a message naming its type
const std::vector<std::pair<std::vector<std::uint8_t>, std::string>> cases =
{
{{0xA1, 0x01, 0x01}, "an unsigned integer; last byte: 0x01"},
{{0xA1, 0x20, 0x01}, "a negative integer; last byte: 0x20"},
{{0xA1, 0x41, 0x61, 0x01}, "a byte string; last byte: 0x41"},
{{0xA1, 0x80, 0x01}, "an array; last byte: 0x80"},
{{0xA1, 0xA0, 0x01}, "a map; last byte: 0xA0"},
{{0xA1, 0xC0, 0x61, 0x61, 0x01}, "a tag; last byte: 0xC0"},
{{0xA1, 0xF4, 0x01}, "a boolean; last byte: 0xF4"},
{{0xA1, 0xF5, 0x01}, "a boolean; last byte: 0xF5"},
{{0xA1, 0xF6, 0x01}, "null; last byte: 0xF6"},
{{0xA1, 0xF7, 0x01}, "undefined; last byte: 0xF7"},
{{0xA1, 0xF9, 0x3C, 0x00, 0x01}, "a floating-point number; last byte: 0xF9"},
{{0xA1, 0xFA, 0x3F, 0x80, 0x00, 0x00, 0x01}, "a floating-point number; last byte: 0xFA"},
{{0xA1, 0xFB, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, "a floating-point number; last byte: 0xFB"},
{{0xA1, 0xE0, 0x01}, "a simple value; last byte: 0xE0"},
{{0xA1, 0xF8, 0x20, 0x01}, "a simple value; last byte: 0xF8"},
// indefinite-length map
{{0xBF, 0x01, 0x01, 0xFF}, "an unsigned integer; last byte: 0x01"},
};
for (const auto& c : cases)
{
CAPTURE(c.first)
const std::string expected = "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR object key: only string keys are supported, but found " + c.second;
json _;
CHECK_THROWS_WITH_AS(_ = json::from_cbor(c.first), expected.c_str(), json::parse_error&);
CHECK(json::from_cbor(c.first, true, false).is_discarded());
}
// a key of major type 3 with a reserved length is still reported as
// a malformed string, and a missing key as the end of input
json _;
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0xA1})), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&);
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0xA1, 0x7C, 0x01})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x7C", json::parse_error&);
}
SECTION("invalid UTF-8 in string (see #5529)") SECTION("invalid UTF-8 in string (see #5529)")
{ {
// a two-character text string (major type 3) whose bytes are not // a two-character text string (major type 3) whose bytes are not
@@ -2284,7 +2325,7 @@ TEST_CASE("CBOR indefinite-length strings do not recurse per chunk")
SECTION("a break marker outside an indefinite-length string is not a string") SECTION("a break marker outside an indefinite-length string is not a string")
{ {
// 0xFF only closes a string that was opened; on its own it is not one // 0xFF only closes a string that was opened; on its own it is not one
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0xA1, 0xFF, 0x01})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0xFF", json::parse_error&); CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0xA1, 0xFF, 0x01})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR object key: only string keys are supported, but found a break stop code; last byte: 0xFF", json::parse_error&);
} }
} }

Some files were not shown because too many files have changed in this diff Show More