Skip to content

Review and extend the documentation, and check it in CI - #5638

Merged
nlohmann merged 8 commits into
developfrom
docs/review-fixes
Oct 2, 2026
Merged

nlohmann merged 8 commits into
developfrom
docs/review-fixes

Conversation

@nlohmann

@nlohmann nlohmann commented Sep 29, 2026 •

Copy link
Copy Markdown
Owner

A review of all 263 documentation pages found factual errors, dead links, missing cross-references, and gaps in
examples and diagrams. This PR fixes them and adds checks so the same kinds of problems are caught automatically.

Fixes

  • Wrong signatures and version histories, each verified against the headers and release tags:
  • Stale descriptions:
    • Number parsing still mentioned strtoull/strtoll, replaced in Speed up parsing of contiguous input (numbers, strings, UTF-8) #5283.
    • The UBJSON type table had the wrong high-precision range.
    • The SAX error-handling example no longer compiled. It is now a runnable example checked by CI.
    • ordered_map.md recommended tsl::ordered_map, which the other pages say cannot be used.
  • Dead links:
    • Internal: #overload-4, and a Doxygen-era link in the FAQ.
    • External: customer entries get a current source where one was found. Customer links without a working
      replacement are kept and excluded from the link check by exact URL. The deleted sponsor profile is plain text.
    • Header @sa URLs that only worked through redirects.
  • repology.org is suspended at the registrar (DNS resolves to 127.0.0.1), so its badges no longer render. They are
    replaced by shields.io badges that query the package registries directly.
  • Deprecations: every deprecation note now links the matching section of the migration guide. The deprecated
    json_pointer<basic_json> overloads of at, contains, operator[], and value, and the legacy operator<=/>=
    members, are now documented. The migration guide itself is fixed (detail, not details namespace; the
    pointer/length overloads of from_*).
  • Consistency: all example admonitions follow the "Example: (N) ..." / "Example: ..." title style.
  • Other fixes: the cget example included the vcpkg files, a Bazel block was labeled WORKSPACE, heading levels
    were skipped, and images had no alt text.

Additions

  • Cross-references:
    • About 90 new "See also" sections.
    • Links on first mentions of API functions, macros, and exception ids.
    • Integration pages link each other.
  • Examples:
    • 25 runnable examples, mostly error paths: exceptions of to_bson/to_msgpack/..., partially applied
      patch_inplace, value() type errors, discarded values, overloads without an example.
    • 12 Mermaid diagrams: conversion dispatch, SAX event order, macro choice, parse flow, integration choice, ...
  • New API pages: json_pointer::operator<=>, byte_container_with_subtype::operator==/!=.
  • New pages:
    • A landing page with a quick start.
    • Guides for parsing untrusted input and for performance.
    • Swift Package Manager and MSYS2 instructions.
    • "Exception safety" and "Complexity" sections where missing.
  • "Unreleased" badge: a build hook (docs/mkdocs/hooks/unreleased_versions.py) marks every "version X.Y.Z" newer
    than the version in abi_macros.hpp. Today that is 3.13.0; the badges disappear after the release.

Checks

  • Strict documentation build: make build now runs mkdocs build --strict with link and anchor validation, so
    broken links fail CI. A shallow clone makes the git-revision-date plugin warn, which fails the strict build. The
    docs CI job and the publish workflow therefore fetch the full history, which also fixes the identical "last updated"
    dates on the published site.
  • New workflow check_docs_links.yml: a weekly (and manual) external link check. It reports all broken links
    and ignores bot blocking and rate limiting.
  • New CI target ci_test_documentation_mermaid: parses all Mermaid diagrams (Node, mermaid.parse with jsdom).
  • check_structure.py:
    • New checks: example titles, heading levels, image alt text, header @sa links, and docset index coverage.
    • The docset index docSet.sql gets 35 missing entries.
    • Its "unused example" check never ran (wrong glob since Reorganize directories #3462); it runs again.
  • Portable examples: four examples printed unordered containers and are now deterministic, so all examples also
    pass check_output on macOS.
  • scripts/check_version_history.py: a manual, report-only check of the macro pages' "Added in version" entries
    against the release tags.

Notes for reviewers

  • What to read: most of the diff is mechanical ("See also", links, titles). The new prose is in
    features/parsing/untrusted_input.md, features/performance.md, index.md, and the NuGet and Android FAQ
    rewrites.
  • Headers: include/ changes are comments only (@sa URLs, @since, parser class comment); single_include is
    re-amalgamated.
  • Share one nesting depth limit between all bounded descents #5637: it unifies the nesting depth limits. If it lands first, update the depth-limit paragraph of
    untrusted_input.md (or vice versa).
  • README: the repology badge is removed from README.md.
  • Link check workflow: its manual trigger works only once the workflow is on develop.

Public API

No breaking changes. This PR changes documentation, documentation tooling, CI configuration, and comments in the
headers only; no code of the library changes.


  • The changes are described in detail, both the what and why.
  • If applicable, an existing issue is referenced.
  • If applicable, a fixed OSS-Fuzz issue is referenced as OSS-Fuzz: <id>.
  • The Code coverage remained at 100%. A test case for every new line of code.
  • If applicable, the documentation is updated.
  • The source code is amalgamated by running make amalgamate.

This pull request was written by Claude Code.

🤖 Generated with Claude Code

A review of all documentation pages found factual errors, dead links,
missing cross-references, and gaps in examples. This fixes them and adds
checks so the same problems are caught automatically.

Fixes:
- wrong signatures and version histories (operator!= C++20 member,
  binary() subtype type, get<PointerType>(), JSON_NO_THREAD_LOCAL, ...)
- stale descriptions (number parsing since #5283, UBJSON table, SAX
  example that no longer compiled, tsl::ordered_map advice)
- dead internal and external links; repology.org badges (the domain is
  suspended) replaced by badges that query the registries directly
- deprecation notes link the migration guide; the guide itself fixed

Additions:
- "See also" sections, cross-references, 25 runnable examples, 12
  Mermaid diagrams, new API pages for json_pointer::operator<=> and
  byte_container_with_subtype::operator==/!=
- landing page, guides for untrusted input and performance
- "unreleased" badge after versions newer than the latest release

Checks:
- strict documentation build (broken links/anchors fail it); CI and
  the publish workflow fetch the full history the build needs
- weekly external link check, Mermaid syntax check in CI
- check_structure.py: example titles, heading levels, alt texts,
  header links, docset index coverage; its unused-example check works
  again
- all examples produce the same output on every platform

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
A dead link on the customers page is still the evidence of where the
use of the library was documented. Keep the original URLs of the entries
without a working replacement (Marne, Cisco Webex Desk Camera, Philips
Hue, CyberArk) and exclude exactly these URLs from the link check.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
The SAX interface's key() receives no position either; only parse_error()
does. Also note that the recipe does not report the path to the repeated
key (see discussion #5085).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Comment thread docs/mkdocs/docs/index.md Outdated
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
- patch/patch_inplace: list the JSON pointer errors parse_error.106-109
  and out_of_range.402/404, and quote the actual parse_error.105 message.
- unflatten: list parse_error.106/107/108 and out_of_range.404.
- to_bson: list out_of_range.415 (binary subtype above 255) and note
  that 412 and 415 are new in 3.13.0.
- to_string: state that string_t must be convertible to std::string, also
  in the StringType requirements table.
- JSON Lines: a `while (input >> j)` loop also throws after the last value
  for concatenated JSON values; show a loop that works for both.
- BON8: a string gets 0xFF only if nothing follows it in the message; a
  string at the end of an array or object is ended by 0xFE.
- custom_string_type.hpp: add operator+=(char), which the "Always
  required" list asks for (json_pointer::to_string, flatten, unflatten,
  and diff did not compile), and an ADL int_to_string for diff and items.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Codacy (Pylint) flagged the mutable default argument that header() used
as its cache. functools.lru_cache keeps the same memoization without it.
The script's output is unchanged.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
@nlohmann nlohmann added the review needed It would be great if someone could review the proposed changes. label Sep 29, 2026
@github-actions github-actions Bot added documentation aspect: binary formats BSON, CBOR, MessagePack, UBJSON L CI CMake python Pull requests that update Python code labels Sep 30, 2026
nlohmann added a commit that referenced this pull request Sep 30, 2026
The checkout step in publish_documentation.yml left the default
persist-credentials: true, so GITHUB_TOKEN stayed writable in
.git/config for the rest of the job (zizmor's artipacked finding). The
Deploy documentation step authenticates through its own github_token
input to peaceiris/actions-gh-pages and does not push with the
checked-out credentials, so persist-credentials: false is safe here,
matching every other checkout in the workflow set.

Overlaps #5638, which edits this same checkout step (adds
fetch-depth: 0); expect a rebase conflict there.

Part of #5718 item 2

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
nlohmann added a commit that referenced this pull request Sep 30, 2026
The docset Makefile's list_missing_pages ran one sqlite3 query per
mkdocs page, and list_removed_paths nested a loop over all mkdocs pages
inside a loop over all docset index paths (O(n*m) shell iteration).
Issue #5718 item 5 suggested removing or reducing these targets once
#5638's check_docset() lands, but that PR is still open and covers only
API pages and macros, not the full page set these targets check.
Replace the loops with two sorted path lists (DOCSET_PAGE_PATHS from
mkdocs' markdown sources, DOCSET_INDEX_PATHS from the built docset
index) compared with a single comm(1) call each, verified to produce
output identical to the old loops against the current docSet.dsidx.

The sed expression used '#' as its delimiter, which GNU Make reads as
a comment character even inside a variable assignment, truncating the
line and orphaning the closing paren of $(shell ...) ("unterminated
call to function 'shell': missing ')'"). Use '@' as the delimiter
instead.

Part of #5718 item 5

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Conflicts:
- include/nlohmann/json.hpp: took develop's removal of the UDLs (moved to json_literals.hpp, which already has the corrected @sa links)
- single_include/nlohmann/json.hpp: regenerated with make amalgamate
- docs/mkdocs/docs/api/operator_literal_json.md, operator_literal_json_pointer.md: kept the PR's migration guide link and develop's json_literals.hpp/JSON_NO_AUTOMATIC_UDLS note
- docs/mkdocs/docs/api/basic_json/to_bson.md: both sides added out_of_range.415; kept develop's example message and both version history lines
- docs/docset/docSet.sql: added JSON_NO_AUTOMATIC_UDLS (new on develop), which the PR's check_structure.py requires

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

Copy link
Copy Markdown
Owner Author

Merged develop (289d61e). Develop moved the user-defined literals to <nlohmann/json_literals.hpp>, so the conflicting UDL block in json.hpp was dropped; the new header already has the corrected @sa links. The literal docs pages keep this PR's migration guide link plus develop's note on json_literals.hpp/JSON_NO_AUTOMATIC_UDLS. I also added JSON_NO_AUTOMATIC_UDLS (new on develop) to docs/docset/docSet.sql, because this PR's check_structure.py requires an entry for every page.

— posted by Claude Code on behalf of @nlohmann

nlohmann added a commit that referenced this pull request Sep 30, 2026
* Fix math formulas rendering as raw TeX in the published docs

The privacy plugin self-hosts MathJax 2.7.0 but drops its
?config=TeX-MML-AM_CHTML query string, so the rehosted script loads no
input jax and the 10 formulas across 6 pages render as raw TeX to
readers. Remove pymdownx.arithmatex and the MathJax extra_javascript
entry, and rewrite the formulas in plain HTML (<sup>, <i>) instead.
This also drops a nine-year-old third-party script from every page.

Part of #5718

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

* Fix publish_documentation triggers and persist unneeded git credentials

publish_documentation.yml only triggered on docs/mkdocs/** pushes, but
the site also embeds .github/CODE_OF_CONDUCT.md, CONTRIBUTING.md,
SECURITY.md, cmake/{clang,gcc}_flags.cmake, .clang-tidy,
tools/astyle/.astylerc and tests/fmt_formatter/project/main.cpp via
pymdownx.snippets, so changes to those files never republished the
site. Extend the path filter to cover them, and switch runs-on from
the long-pinned ubuntu-22.04 to ubuntu-latest to match
ci_test_documentation.

Also add persist-credentials: false to the checkouts in ci_icpx and
ci_nvhpc (ubuntu.yml) and msvc-vs2026/msvc-arm64 (windows.yml), none
of which pushes with git, matching every other checkout in these
workflows.

Part of #5718

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

* Fix example build: broken debug echo, deprecations hidden for everything

docs/Makefile's debug echo used a space instead of a comma in
$(call cxx_standard ...), so it always printed an empty standard.
Every example was also compiled with -Wno-deprecated-declarations,
which would silently hide an accidental deprecated-API call in any of
them. Factor the duplicated compile flags into EXAMPLE_CPPFLAGS/
EXAMPLE_WARNFLAGS, build with -Werror=deprecated-declarations by
default, and only allow the three examples that intentionally
document deprecated API (the DEPRECATED_EXAMPLES list) to suppress it.

Part of #5718

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

* Fix check_structure.py NOLINT parsing and an off-by-one report line

`line.strip("<!-- NOLINT")` followed by `.strip(" -->")` strips any of
the characters in those sets from both ends, not a literal prefix; it
only happened to work for "Examples". A NOLINT'd section name
starting with N, O, L, I or T (e.g. "Notes", "Template parameters",
"Iterator invalidation", "Literals") was silently mangled, so the
suppression did not apply and the checker could report a spurious
missing/misordered section. Parse the comment with a regex instead.
The same fragile strip() pattern was used for heading text; replace it
with a plain prefix slice. Also fix the admonition_title report, which
used the 0-based line index while every other report in the file uses
lineno+1.

Part of #5718

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

* Fix docset README's non-existent make target and stale fallback URL

The README told readers to run `make nlohmann_json.docset`, but the
Makefile's targets are `all`, `JSON_for_Modern_C++.docset` and
`install_docset_zeal`; the documented command has failed with "No
rule to make target" since #2967 (2021). Point the README at the real
target and folder name. Info.plist's DashDocSetFallbackURL also still
pointed at the old nlohmann.github.io/json/ URL instead of the
canonical https://json.nlohmann.me/ from mkdocs.yml's site_url.

Leave list_missing_pages/list_removed_paths alone: they may become
redundant once #5638's check_docset() lands, which is a follow-up.

Part of #5718

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

* Remove two leftover Doxygen-era .link files from the examples directory

parse__iterator_pair.link and parse__pointers.link each held only a
Wandbox "online" permalink from the old Doxygen docs. #3071 deleted
every other .link file in 2021; these two came in through a parallel
PR (#3100) and were never referenced by any page, script or config.

Part of #5718

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

* Fix MacPorts CMake example to include its own snippet files

The MacPorts "Example: CMake" block included
integration/homebrew/example.cpp and
integration/homebrew/CMakeLists.txt instead of the MacPorts files
right next to it, a copy-paste slip from the Homebrew section. Nothing
referenced integration/macports/CMakeLists.txt as a result. The page
rendered correctly only because the homebrew, macports and
vcpkg/CMakeLists.txt snippets are byte-identical, so a future edit to
the MacPorts files would not have shown up on the page.

Part of #5718 item 6

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

* Do not persist git credentials in publish_documentation's checkout

The checkout step in publish_documentation.yml left the default
persist-credentials: true, so GITHUB_TOKEN stayed writable in
.git/config for the rest of the job (zizmor's artipacked finding). The
Deploy documentation step authenticates through its own github_token
input to peaceiris/actions-gh-pages and does not push with the
checked-out credentials, so persist-credentials: false is safe here,
matching every other checkout in the workflow set.

Overlaps #5638, which edits this same checkout step (adds
fetch-depth: 0); expect a rebase conflict there.

Part of #5718 item 2

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

* Replace list_missing_pages/list_removed_paths with a comm(1)-based diff

The docset Makefile's list_missing_pages ran one sqlite3 query per
mkdocs page, and list_removed_paths nested a loop over all mkdocs pages
inside a loop over all docset index paths (O(n*m) shell iteration).
Issue #5718 item 5 suggested removing or reducing these targets once
#5638's check_docset() lands, but that PR is still open and covers only
API pages and macros, not the full page set these targets check.
Replace the loops with two sorted path lists (DOCSET_PAGE_PATHS from
mkdocs' markdown sources, DOCSET_INDEX_PATHS from the built docset
index) compared with a single comm(1) call each, verified to produce
output identical to the old loops against the current docSet.dsidx.

The sed expression used '#' as its delimiter, which GNU Make reads as
a comment character even inside a variable assignment, truncating the
line and orphaning the closing paren of $(shell ...) ("unterminated
call to function 'shell': missing ')'"). Use '@' as the delimiter
instead.

Part of #5718 item 5

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

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
@nlohmann nlohmann added this to the Release 3.13.0 milestone Oct 2, 2026
@nlohmann nlohmann added 🚀 ready to merge Ready to merge - just waiting for CI to complete. and removed review needed It would be great if someone could review the proposed changes. labels Oct 2, 2026
@nlohmann nlohmann mentioned this pull request Oct 2, 2026
Conflicts, each resolved by keeping both sides:
- publish_documentation.yml: develop's persist-credentials: false and
  this branch's fetch-depth: 0.
- mkdocs.yml: develop's JSON_DISABLE_TUPLE_REFERENCE_CONVERSION entry
  and this branch's JSON_HAS_CPP_23/26 title.
- parser.hpp: combined the two rewordings of the class comment.

Also prefixed the two example titles of the new
json_disable_tuple_reference_conversion.md page with "Example:" for
this branch's check_structure.py, and ran make amalgamate (keeping this
branch's fixed api/operator_ltlt and api/operator_gtgt links).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
@nlohmann
nlohmann merged commit 63c10a5 into develop Oct 2, 2026
56 of 164 checks passed
@nlohmann
nlohmann deleted the docs/review-fixes branch October 2, 2026 09:32
nlohmann added a commit that referenced this pull request Oct 2, 2026
Conflict in operator_ne.md: took develop's corrected page (#5638 documents that there is no C++20 member operator!=) and re-added this branch's conditional noexcept for overload (2) and its version history note. Ran make amalgamate.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
nlohmann added a commit that referenced this pull request Oct 4, 2026
- Swift docs: take develop's reviewed version from #5638, which documents
  #4650 with the --build-system native workaround; the swiftpm CI job
  added here only copies example.cpp into a library target and still
  works with it
- .reuse/dep5 stays deleted (migrated to REUSE.toml here); develop's
  removal of the stale tests/thirdparty/Fuzzer entry is applied to
  REUSE.toml instead

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
nlohmann added a commit that referenced this pull request Oct 4, 2026
- ordered_map.hpp: develop's refactored members, with this branch's
  @sa links on every public member
- byte_container_with_subtype operator==/!= and ordered_map docs:
  develop's reviewed text (#5638); drop the now unreferenced examples
- basic_json.md: keep both version-history additions
- add the docset entries that develop's style check now requires for the
  25 API pages this branch adds
- regenerate tools/api_checker/api_surface.json for develop's API

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

aspect: binary formats BSON, CBOR, MessagePack, UBJSON CI CMake documentation L python Pull requests that update Python code 🚀 ready to merge Ready to merge - just waiting for CI to complete.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants