Compare commits

..
Author SHA1 Message Date
Niels Lohmann 4006a406a7 Merge develop; fix clang-tidy findings in json_view
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-25 19:03:32 +01:00
Niels Lohmann 2bc2f7d09d json_view: fewer allocations when parsing small documents
- Size the buffer of decoded strings on its first growth from the input (a
  quarter of it plus 16 bytes, at most 4 KiB) instead of 272 bytes, so that a
  document with escapes does not grow it twice, with a zero-filled copy each
  time.
- Allocate the node index of documents with an estimate of up to 1024 nodes
  (was 256) together with the header, which saves one allocation. When the
  estimate is too low (dense numbers), that block stays unused: at most 16 KiB
  instead of 4 KiB. shrink_to_fit() cannot release it separately; the
  documentation says so.

Fresh parse of a 2.5 KB minified tweet: 993 to 958 ns (-3.5 %), traverse and
select -3 %, with no row consistently slower (Apple M1 Max, 30 rounds, three
runs). Dense numeric documents parse about 9 % faster and keep up to 16 KiB
more memory.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-25 11:30:30 +01:00
48 changed files with 1882 additions and 2319 deletions

No files matched your search

@@ -113,15 +113,15 @@ basic_json(basic_json&& other) noexcept;
```mermaid
flowchart TD
A(["initializer_list init"]) --> B{{"empty, or every element is a 2-element<br/>array whose first element is a string?"}}
B -->|"yes"| C{{"type_deduction"}}
B -->|"no"| D{{"type_deduction"}}
A(["initializer_list init"]) --> B{"empty, or every element is a 2-element<br/>array whose first element is a string?"}
B -->|"yes"| C{"type_deduction"}
B -->|"no"| D{"type_deduction"}
C -->|"true"| OBJ["create object"]
C -->|"false"| E{{"manual_type"}}
C -->|"false"| E{"manual_type"}
E -->|"object"| OBJ
E -->|"array"| ARR["create array"]
D -->|"true"| ARR
D -->|"false"| F{{"manual_type"}}
D -->|"false"| F{"manual_type"}
F -->|"array"| ARR
F -->|"object"| ERR["throw type_error.301"]
```
+1 -3
View File
@@ -7,9 +7,7 @@ const_iterator begin() const noexcept;
Returns an iterator to the first element.
<div class="nj-fig-wrap" markdown>
--8<-- "images/range-begin-end.svg"
</div>
![Illustration from cppreference.com](../../images/range-begin-end.svg)
## Return value
+1 -3
View File
@@ -6,9 +6,7 @@ const_iterator cbegin() const noexcept;
Returns an iterator to the first element.
<div class="nj-fig-wrap" markdown>
--8<-- "images/range-begin-end.svg"
</div>
![Illustration from cppreference.com](../../images/range-begin-end.svg)
## Return value
+1 -3
View File
@@ -6,9 +6,7 @@ const_iterator cend() const noexcept;
Returns an iterator to one past the last element.
<div class="nj-fig-wrap" markdown>
--8<-- "images/range-begin-end.svg"
</div>
![Illustration from cppreference.com](../../images/range-begin-end.svg)
## Return value
+1 -3
View File
@@ -6,9 +6,7 @@ const_reverse_iterator crbegin() const noexcept;
Returns an iterator to the reverse-beginning; that is, the last element.
<div class="nj-fig-wrap" markdown>
--8<-- "images/range-rbegin-rend.svg"
</div>
![Illustration from cppreference.com](../../images/range-rbegin-rend.svg)
## Return value
+1 -3
View File
@@ -7,9 +7,7 @@ const_reverse_iterator crend() const noexcept;
Returns an iterator to the reverse-end; that is, one before the first element. This element acts as a placeholder,
attempting to access it results in undefined behavior.
<div class="nj-fig-wrap" markdown>
--8<-- "images/range-rbegin-rend.svg"
</div>
![Illustration from cppreference.com](../../images/range-rbegin-rend.svg)
## Return value
+1 -3
View File
@@ -7,9 +7,7 @@ const_iterator end() const noexcept;
Returns an iterator to one past the last element.
<div class="nj-fig-wrap" markdown>
--8<-- "images/range-begin-end.svg"
</div>
![Illustration from cppreference.com](../../images/range-begin-end.svg)
## Return value
@@ -22,9 +22,7 @@ The parser callback distinguishes the following events:
## Examples
<div class="nj-fig-wrap" markdown>
--8<-- "images/callback_events.svg"
</div>
![Example when certain parse events are triggered](../../images/callback_events.png)
??? example
@@ -23,9 +23,7 @@ table describes the values of the parameters `depth`, `event`, and `parsed`.
| `parse_event_t::array_end` | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array |
| `parse_event_t::value` | the parser finished reading a JSON value | depth of the value | the parsed JSON value |
<div class="nj-fig-wrap" markdown>
--8<-- "images/callback_events.svg"
</div>
![Example when certain parse events are triggered](../../images/callback_events.png)
Discarding a value (i.e., returning `#!cpp false`) has different effects depending on the context in which function was
called:
+1 -3
View File
@@ -7,9 +7,7 @@ const_reverse_iterator rbegin() const noexcept;
Returns an iterator to the reverse-beginning; that is, the last element.
<div class="nj-fig-wrap" markdown>
--8<-- "images/range-rbegin-rend.svg"
</div>
![Illustration from cppreference.com](../../images/range-rbegin-rend.svg)
## Return value
+1 -3
View File
@@ -8,9 +8,7 @@ const_reverse_iterator rend() const noexcept;
Returns an iterator to the reverse-end; that is, one before the first element. This element acts as a placeholder,
attempting to access it results in undefined behavior.
<div class="nj-fig-wrap" markdown>
--8<-- "images/range-rbegin-rend.svg"
</div>
![Illustration from cppreference.com](../../images/range-rbegin-rend.svg)
## Return value
@@ -23,6 +23,10 @@ Linear in [`node_count()`](node_count.md) plus the length of the decoded strings
## Notes
The node index of a small document (an estimate of up to 1024 nodes, which is up to about 4 KiB of minified text) is
allocated together with the document's header, so that parsing needs one allocation. That part cannot be released
separately: `shrink_to_fit()` only trims a node index that has its own allocation, and the decoded strings.
!!! warning "Invalidates views"
Unlike moving the document, `shrink_to_fit()` **invalidates every view taken from this document before the
-739
View File
@@ -1,739 +0,0 @@
/* nlohmann/json brand for Material for MkDocs (values from brand/kit/tokens.css) */
@font-face {
font-family: "Inter";
src: url("fonts/Inter.woff2") format("woff2");
font-weight: 100 900;
font-display: swap;
}
@font-face {
font-family: "Inter";
src: url("fonts/Inter-Italic.woff2") format("woff2");
font-style: italic;
font-weight: 100 900;
font-display: swap;
}
@font-face {
font-family: "JetBrains Mono";
src: url("fonts/JetBrainsMono.woff2") format("woff2");
font-weight: 100 800;
font-display: swap;
}
@font-face {
font-family: "JetBrains Mono";
src: url("fonts/JetBrainsMono-Italic.woff2") format("woff2");
font-style: italic;
font-weight: 400;
font-display: swap;
}
:root {
--md-text-font: "Inter";
--md-code-font: "JetBrains Mono";
}
/* light */
[data-md-color-scheme="default"] {
--nj-blue: #1747a6;
--nj-blue-deep: #10357d;
--nj-blue-soft: #eaf1fd;
--nj-band: #1747a6;
--nj-red: #d93a3f;
--nj-red-soft: #fdeced;
--nj-string: #b4322a;
--nj-comment: #646d7a;
--md-primary-fg-color: var(--nj-band);
--md-primary-fg-color--light: #1d55c4;
--md-primary-fg-color--dark: #10357d;
--md-accent-fg-color: var(--nj-blue-deep);
--md-accent-fg-color--transparent: rgba(23, 71, 166, .1);
--md-typeset-a-color: var(--nj-blue);
--md-default-fg-color: #0f1419;
--md-default-fg-color--light: #4a515c;
--md-default-fg-color--lighter: #8a919c;
--md-default-fg-color--lightest: #e3e6eb;
--md-typeset-color: #0f1419;
--md-code-bg-color: #f5f7fa;
--md-code-fg-color: #0f1419;
}
/* dark */
[data-md-color-scheme="slate"] {
--nj-blue: #7aa7f5;
--nj-blue-deep: #a9c6f8;
--nj-blue-soft: #16233b;
--nj-band: #173e8c;
--nj-red: #f06a6e;
--nj-red-soft: #3a1c1e;
--nj-string: #f08a7e;
--nj-comment: #8b929e;
--md-hue: 220;
--md-primary-fg-color: var(--nj-band);
--md-primary-fg-color--light: #163f93;
--md-primary-fg-color--dark: #0c2556;
--md-accent-fg-color: var(--nj-blue-deep);
--md-accent-fg-color--transparent: rgba(122, 167, 245, .12);
--md-typeset-a-color: var(--nj-blue);
--md-default-bg-color: #0f1218;
--md-default-fg-color: #f2f4f7;
--md-default-fg-color--light: #b3b9c4;
--md-default-fg-color--lighter: #7b828e;
--md-default-fg-color--lightest: #272c36;
--md-typeset-color: #f2f4f7;
--md-code-bg-color: #171b23;
--md-code-fg-color: #f2f4f7;
--md-footer-bg-color: #0b0e13;
--md-footer-bg-color--dark: #08090d;
}
/* syntax colours (all at least 4.5:1 on the code panel in both schemes) */
[data-md-color-scheme="default"] {
--nj-hl-keyword: #1747a6;
--nj-hl-type: #0d6e72;
--nj-hl-function: #6b3fb8;
--nj-hl-string: #23793a;
--nj-hl-escape: #0f5d2a;
--nj-hl-key: #10357d;
--nj-hl-number: #b04a07;
--nj-hl-preproc: #a3247a;
}
[data-md-color-scheme="slate"] {
--nj-hl-keyword: #7aa7f5;
--nj-hl-type: #4fd1c5;
--nj-hl-function: #c4a3ff;
--nj-hl-string: #7ed491;
--nj-hl-escape: #b2ecc0;
--nj-hl-key: #a9c6f8;
--nj-hl-number: #f5a65b;
--nj-hl-preproc: #f48fc8;
}
.md-typeset .highlight :is(.k, .kd, .kn, .kr, .kp, .kv, .ow) { color: var(--nj-hl-keyword); font-weight: 600; }
.md-typeset .highlight :is(.kt, .nc, .nn, .na) { color: var(--nj-hl-type); }
.md-typeset .highlight :is(.nf, .fm, .nb, .bp) { color: var(--nj-hl-function); }
.md-typeset .highlight :is(.s, .s1, .s2, .sb, .sc, .sd, .sh, .sx, .sr, .ss, .sa, .dl, .cpf) { color: var(--nj-hl-string); }
.md-typeset .highlight :is(.se, .si) { color: var(--nj-hl-escape); font-weight: 600; }
.md-typeset .highlight .nt { color: var(--nj-hl-key); }
.md-typeset .highlight :is(.m, .mi, .mf, .mh, .mo, .mb, .il, .kc) { color: var(--nj-hl-number); }
.md-typeset .highlight .kc { font-weight: 500; }
.md-typeset .highlight .cp { color: var(--nj-hl-preproc); }
.md-typeset .highlight :is(.c, .c1, .cm, .ch, .cs) { color: var(--nj-comment); font-style: italic; }
.md-typeset .highlight :is(.o, .p) { color: var(--md-default-fg-color--light); }
.md-typeset .highlight :is(.n, .nx, .nv, .no, .ni, .nl, .py) { color: var(--md-code-fg-color); }
.md-typeset .highlight .err { color: inherit; background: none; }
/* typography: Inter with cv11/ss03, bold headlines with negative tracking */
body {
font-feature-settings: "cv11", "ss03";
}
.md-typeset {
font-size: .85rem; /* 17 px body */
line-height: 1.65;
}
.md-typeset h1 {
color: var(--md-typeset-color);
font-weight: 800;
letter-spacing: -.035em;
line-height: 1.1;
}
.md-typeset h2 {
font-weight: 800;
letter-spacing: -.025em;
line-height: 1.15;
}
.md-typeset h3 {
font-weight: 700;
letter-spacing: -.015em;
}
.md-typeset table:not([class]) {
font-variant-numeric: tabular-nums;
}
/* header and tabs: one blue band with the faint syntax pattern; wordmark instead of the site name.
A veil over the tile keeps the glyphs from running through the navigation text; the hero shows the pattern at full strength. */
.md-header, .md-tabs {
--nj-veil: linear-gradient(color-mix(in srgb, var(--md-primary-fg-color) 62%, transparent), color-mix(in srgb, var(--md-primary-fg-color) 62%, transparent));
background-color: var(--md-primary-fg-color);
background-image: var(--nj-veil), url("pattern-on-blue.svg");
background-size: auto, 160px 160px;
background-position: 0 0, 0 0;
}
.md-tabs {
background-position: 0 0, 0 -48px; /* continue the header's tile */
}
.md-header__button.md-logo img {
width: auto;
height: 1.5rem;
}
.md-header__topic:first-child .md-ellipsis { /* the wordmark replaces the site name at every width */
display: none;
}
@media screen and (max-width: 76.234375em) { /* Material hides the header logo here: show the wordmark on phones, too */
.md-header__button.md-logo {
display: block;
margin: 0 0 0 .2rem;
padding: .4rem .2rem;
}
.md-header__button[for="__drawer"] {
order: -1;
}
}
.md-nav__button.md-logo img {
width: auto;
height: 1.4rem;
}
/* shapes: 6 px corners on blocks, 4 px on inline code */
.md-typeset pre > code, .md-typeset .highlight, .md-typeset .highlighttable,
.md-typeset .admonition, .md-typeset details, .md-typeset .tabbed-set,
.md-typeset .grid.cards > ul > li, .md-typeset .md-button {
border-radius: 6px;
}
.md-typeset code {
border-radius: 4px;
}
/* inline code: the font alone marks it, no grey box; linked code keeps the link colour */
.md-typeset :not(pre) > code {
padding: 0;
background-color: transparent;
box-shadow: none;
color: inherit;
font-size: .88em;
}
.md-typeset .highlight pre > code, .md-typeset .highlighttable {
border: 1px solid var(--md-default-fg-color--lightest);
}
/* callouts: 3 px left border; note/info/tip in blue on blue-soft, danger/bug in red on red-soft */
.md-typeset .admonition, .md-typeset details {
border-width: 0 0 0 3px;
box-shadow: none;
font-size: .8rem;
}
.md-typeset :is(.admonition, details):is(.note, .info, .tip, .abstract, .example, .question) {
border-color: var(--nj-blue);
background-color: var(--nj-blue-soft);
}
.md-typeset :is(.note, .info, .tip, .abstract, .example, .question) > :is(.admonition-title, summary) {
background-color: transparent;
}
.md-typeset :is(.note, .info, .tip, .abstract, .example, .question) > :is(.admonition-title, summary)::before {
background-color: var(--nj-blue);
}
.md-typeset :is(.admonition, details):is(.danger, .bug, .failure) {
border-color: var(--nj-red);
background-color: var(--nj-red-soft);
}
.md-typeset :is(.danger, .bug, .failure) > :is(.admonition-title, summary) {
background-color: transparent;
}
.md-typeset :is(.danger, .bug, .failure) > :is(.admonition-title, summary)::before {
background-color: var(--nj-red);
}
/* buttons */
.md-typeset .md-button--primary {
background-color: var(--nj-blue);
border-color: var(--nj-blue);
}
/* home page hero (overrides/home.html): blue band with the syntax pattern, headline, lead, buttons, code panel */
.nj-hero {
--nj-hero-to: #123a8a;
color: #fff;
background-color: var(--md-primary-fg-color);
background-image: url("pattern-on-blue.svg"), linear-gradient(160deg, var(--md-primary-fg-color), var(--nj-hero-to));
background-size: 160px 160px, auto;
background-position: 0 -96px, 0 0; /* continue the tile of header and tabs */
}
[data-md-color-scheme="slate"] .nj-hero {
--nj-hero-to: #0c2556;
}
.nj-hero__inner {
display: grid;
grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
gap: 2.4rem;
align-items: center;
padding: 3rem .8rem 3.6rem;
}
.nj-hero__kicker {
display: inline-block;
margin: 0;
padding: .15rem .55rem;
border: 1px solid rgba(255, 255, 255, .35);
border-radius: 4px;
color: #bcd3fb;
font-family: var(--md-code-font-family);
font-size: .65rem; /* 13 px */
font-weight: 500;
letter-spacing: .02em;
}
.nj-hero__title {
margin: 0 0 .8rem;
color: #fff;
font-size: clamp(1.7rem, 4.2vw, 3.2rem); /* 34-64 px */
font-weight: 800;
line-height: 1.02;
letter-spacing: -.035em;
}
.nj-hero__title span {
color: #bcd3fb;
}
.nj-hero__lead {
max-width: 22em;
margin: 0 0 2rem;
color: rgba(255, 255, 255, .9);
font-size: clamp(1.1rem, 2.2vw, 1.6rem); /* 22-32 px */
font-style: italic;
font-weight: 500;
line-height: 1.35;
letter-spacing: -.015em;
}
.nj-hero__actions {
display: flex;
flex-wrap: wrap;
gap: .6rem;
margin: 0;
}
.nj-hero .md-button.nj-hero__button {
margin: 0;
padding: .55em 1.3em;
border: 1px solid rgba(255, 255, 255, .6);
border-radius: 6px;
color: #fff;
font-size: .75rem;
font-weight: 600;
}
.nj-hero .md-button--primary.nj-hero__button {
border-color: #fff;
background-color: #fff;
color: var(--md-primary-fg-color);
}
.nj-hero .md-button.nj-hero__button:is(:hover, :focus) {
border-color: #fff;
background-color: rgba(255, 255, 255, .12);
color: #fff;
}
.nj-hero .md-button--primary.nj-hero__button:is(:hover, :focus) {
background-color: #eaf1fd;
color: var(--md-primary-fg-color);
}
.nj-hero__code {
border-radius: 6px;
background-color: var(--md-code-bg-color);
box-shadow: 0 12px 32px rgba(5, 18, 48, .35);
}
.nj-hero__code pre {
margin: 0;
padding: 1.1rem 1.3rem;
overflow-x: auto;
color: var(--md-code-fg-color);
font-family: var(--md-code-font-family);
font-size: .7rem; /* 14 px */
line-height: 1.7;
}
.nj-hero__code .nj-k { color: var(--nj-hl-type); }
.nj-hero__code .nj-s { color: var(--nj-hl-string); }
.nj-hero__code .nj-n { color: var(--nj-hl-number); }
.nj-hero__code .nj-c { color: var(--nj-comment); font-style: italic; }
@media screen and (max-width: 59.984375em) {
.nj-hero__inner {
grid-template-columns: minmax(0, 1fr);
gap: 1.8rem;
padding: 2rem .8rem 2.4rem;
}
}
@media screen and (max-width: 29.984375em) {
.nj-hero__code pre {
font-size: .6rem;
}
}
/* the hero carries the headline: keep the page's h1 for the title and screen readers only */
body:has(.nj-hero) .md-content__inner > h1 {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
}
/* cards: 1 px line border, 6 px corners, blue icon on soft blue, blue border on hover */
.md-typeset .grid.cards > ul > li {
padding: 1rem 1.1rem;
border: 1px solid var(--md-default-fg-color--lightest);
background-color: var(--md-default-bg-color);
transition: border-color 150ms, box-shadow 150ms, transform 150ms;
}
.md-typeset .grid.cards > ul > li:is(:hover, :focus-within) {
border-color: var(--nj-blue);
box-shadow: 0 6px 18px rgba(23, 71, 166, .12);
transform: translateY(-1px);
}
.md-typeset .grid.cards > ul > li > hr {
display: none;
}
.md-typeset .grid.cards .twemoji.lg {
box-sizing: content-box;
margin-right: .5rem;
padding: .3rem;
border-radius: 6px;
background-color: var(--nj-blue-soft);
color: var(--nj-blue);
}
.md-typeset .grid.cards > ul > li > p:first-child strong {
font-size: .9rem;
font-weight: 700;
letter-spacing: -.01em;
}
.md-typeset .grid.cards > ul > li > p:last-child a {
font-weight: 600;
}
@media (prefers-reduced-motion: reduce) {
.md-typeset .grid.cards > ul > li { transition: none; transform: none !important; }
}
/* home page: no table of contents, so keep the text at reading width and the cards in three columns */
body:has(.nj-hero) .md-content {
max-width: 58rem;
margin: 0 auto;
}
@media screen and (min-width: 60em) {
body:has(.nj-hero) .md-typeset .grid.cards {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
/* lists: Material's indent, bullets as small blue squares, numbers in the secondary text colour */
.md-typeset ul:not(.task-list, .md-annotation-list) > li {
position: relative;
list-style: none;
}
.md-typeset ul:not(.task-list, .md-annotation-list) > li::before {
content: "";
position: absolute;
top: .65em; /* middle of the first line at line-height 1.65 */
left: -.95em;
width: .36em;
height: .36em;
background-color: var(--nj-blue);
}
.md-typeset ol > li::marker {
color: var(--md-default-fg-color--light);
font-variant-numeric: tabular-nums;
}
.md-typeset .grid.cards > ul > li::before {
display: none;
}
/* wider page: Material's grid stops at 61rem (1220 px) */
.md-grid {
max-width: 72rem; /* 1440 px */
}
/* tables: full width, 1 px line border with 6 px corners, header row on the panel colour, 15 px text */
.md-typeset__table {
display: block; /* Material: inline-block, so tables only grow to their content */
}
.md-typeset table:not([class]) {
display: table;
width: 100%;
border: 1px solid var(--md-default-fg-color--lightest);
border-radius: 6px;
font-size: .75rem;
line-height: 1.55;
}
.md-typeset table:not([class]) th {
padding: .65em 1em;
border-bottom: 1px solid var(--md-default-fg-color--lightest);
background-color: var(--md-code-bg-color);
color: var(--md-typeset-color);
font-weight: 600;
vertical-align: bottom;
}
.md-typeset table:not([class]) th:first-child { border-top-left-radius: 5px; }
.md-typeset table:not([class]) th:last-child { border-top-right-radius: 5px; }
.md-typeset table:not([class]) td {
padding: .65em 1em;
border-top: 1px solid var(--md-default-fg-color--lightest);
}
.md-typeset table:not([class]) tbody tr:first-child td {
border-top: 0;
}
.md-typeset table:not([class]) tbody tr:hover {
background-color: var(--nj-blue-soft);
box-shadow: none;
}
.md-typeset table:not([class]) td code {
white-space: nowrap;
}
/* numbers and header code stay on one line; a table wider than the screen scrolls sideways instead */
.md-typeset table:not([class]) :is(td, th):is([align="right"], [style*="right"]),
.md-typeset table:not([class]) th code {
white-space: nowrap;
}
@media screen and (max-width: 44.984375em) {
.md-typeset table:not([class]) {
font-size: .7rem;
}
.md-typeset table:not([class]) :is(th, td) {
padding: .55em .75em;
}
}
/* more room before headings (Material: h2 1.6em, h3 1.6em, h4-h6 1-1.25em) and at the end of each page (1.2rem) */
.md-typeset h2 { margin-top: 2.4em; }
.md-typeset h3 { margin-top: 2.2em; }
.md-typeset h2 + h3 { margin-top: .8em; }
.md-typeset :is(h4, h5, h6) { margin-top: 1.8em; }
.md-content__inner {
margin-bottom: 4.8rem; /* 96 px */
}
/* Mermaid diagrams: white boxes with a blue outline, grey lines, Inter; shapes and spacing in js/mermaid-brand.js */
:root > * {
--md-mermaid-font-family: var(--md-text-font-family), sans-serif;
--md-mermaid-edge-color: var(--md-default-fg-color--lighter);
--md-mermaid-node-bg-color: var(--md-default-bg-color);
--md-mermaid-node-fg-color: var(--nj-blue);
--md-mermaid-label-bg-color: var(--md-default-bg-color);
--md-mermaid-label-fg-color: var(--md-default-fg-color);
--md-mermaid-sequence-actor-bg-color: var(--nj-blue-soft);
--md-mermaid-sequence-actor-line-color: var(--md-default-fg-color--lighter);
--md-mermaid-sequence-note-bg-color: var(--md-code-bg-color);
--md-mermaid-sequence-note-border-color: var(--md-default-fg-color--lightest);
--md-mermaid-sequence-number-fg-color: #fff;
}
/* a diagram wider than the column scrolls sideways (js/mermaid-brand.js stops shrinking it at 80 %) */
.md-typeset .mermaid {
overflow-x: auto;
}
/* ---- polish pass ---- */
/* header: a search field that reads as a field on the band, tabs with an active marker, readable GitHub facts */
.md-header .md-search__form {
border: 1px solid rgba(255, 255, 255, .22);
border-radius: 6px;
background-color: rgba(255, 255, 255, .12);
transition: background-color 150ms, border-color 150ms;
}
.md-header .md-search__form:hover {
border-color: rgba(255, 255, 255, .4);
background-color: rgba(255, 255, 255, .2);
}
.md-search__output {
border-radius: 0 0 6px 6px;
box-shadow: 0 16px 40px rgba(5, 18, 48, .28);
}
.md-search-result__link:focus-visible, .md-search-result__more summary:focus-visible {
outline: 2px solid var(--nj-blue);
outline-offset: -2px;
}
.md-tabs__link {
font-size: .72rem;
font-weight: 500;
opacity: .85;
}
.md-tabs__item--active .md-tabs__link, .md-tabs__link:hover {
opacity: 1;
}
.md-tabs__item--active {
box-shadow: inset 0 -2px 0 #fff;
}
.md-source__facts {
font-size: .62rem;
}
/* footer: deep brand blue instead of neutral black (the dark scheme keeps its own near-black) */
[data-md-color-scheme="default"] {
--md-footer-bg-color: #0d2a66;
--md-footer-bg-color--dark: #0a2152;
}
.md-footer-meta .md-copyright, .md-footer-meta .md-copyright a {
color: rgba(255, 255, 255, .72);
}
.md-footer__link {
border-radius: 6px;
transition: background-color 150ms;
}
.md-footer__link:is(:hover, :focus-visible) {
background-color: rgba(255, 255, 255, .08);
}
/* page title: tighter to the first paragraph; page actions (view/edit source) stay in the background */
.md-typeset h1 {
margin-bottom: 1rem;
}
.md-content__button {
opacity: .55;
transition: opacity 150ms;
}
.md-content__button:is(:hover, :focus-visible) {
opacity: 1;
}
body:has(.nj-hero) .md-content__button {
display: none;
}
/* sidebars: bold marker for the current page, rail and marker for the table of contents */
.md-nav__link--active {
font-weight: 600;
}
.md-nav--secondary > .md-nav__list {
margin-left: -.1rem;
border-left: 1px solid color-mix(in srgb, var(--md-default-fg-color) 16%, transparent);
}
.md-nav--secondary .md-nav__link {
margin-left: -1px;
padding-left: .6rem;
border-left: 2px solid transparent;
}
.md-nav--secondary .md-nav__link--active {
border-left-color: var(--nj-blue);
color: var(--nj-blue);
}
.md-nav--secondary .md-nav__list .md-nav__list {
padding-left: .6rem;
}
.md-nav--secondary .md-nav__title {
font-size: .64rem;
letter-spacing: .04em;
text-transform: uppercase;
}
/* keyboard focus: one visible ring in the brand blue */
.md-typeset a:focus-visible, .md-typeset .md-button:focus-visible, .md-typeset summary:focus-visible,
.md-nav__link:focus-visible, .md-tabs__link:focus-visible, .md-top:focus-visible {
outline: 2px solid var(--nj-blue);
outline-offset: 2px;
border-radius: 3px;
}
.md-header :is(a, button, label):focus-visible, .nj-hero a:focus-visible {
outline: 2px solid #fff;
outline-offset: 2px;
}
/* inline code must not stretch the line box (JetBrains Mono has tall metrics), abbreviations stay quiet */
.md-typeset :not(pre) > code {
line-height: 1;
}
.md-typeset abbr {
border-bottom-color: var(--md-default-fg-color--lighter);
}
.md-typeset :is(h1, h2, h3, h4, a, .grid.cards > ul > li > p:first-child) abbr {
border-bottom-color: transparent;
}
/* number railroad diagram: inline SVG (docs/images/json_syntax_number.svg), follows the colour scheme */
.md-typeset .railroad-wrap, .md-typeset .nj-fig-wrap {
overflow-x: auto;
margin: 1.5rem 0;
}
.md-typeset svg.railroad {
display: block;
width: 100%;
min-width: 36rem;
max-width: 52rem;
margin-inline: auto;
color: var(--md-default-fg-color);
font-family: var(--md-text-font-family), sans-serif;
}
.railroad .rr-t { fill: color-mix(in srgb, var(--md-typeset-a-color) 10%, transparent); stroke: var(--md-typeset-a-color); stroke-width: 2; }
.railroad .rr-track, .railroad .rr-n { stroke: color-mix(in srgb, currentColor 60%, var(--md-default-bg-color)); stroke-opacity: 1; } /* opaque, so overlapping segments do not darken */
.railroad .rr-n { fill: color-mix(in srgb, currentColor 6%, transparent); stroke-width: 2; }
.railroad .rr-bg { fill: var(--md-default-bg-color); }
.railroad .rr-arrow { fill: currentColor; fill-opacity: .6; }
.railroad text { fill: currentColor; text-anchor: middle; }
.railroad .rr-c { font-family: var(--md-code-font-family), monospace; font-size: 16px; font-weight: 500; }
.railroad .rr-b { font-size: 14.5px; font-weight: 500; }
.railroad .rr-l { font-size: 14px; text-anchor: start; fill-opacity: .6; }
.railroad .rr-name { font-size: 17px; font-weight: 700; fill-opacity: 1; }
/* figures drawn as inline SVG (docs/images/callback_events.svg, range-*.svg): they follow the colour scheme */
.md-typeset svg.nj-fig {
display: block;
width: 100%;
height: auto;
margin-inline: auto;
color: var(--md-default-fg-color);
font-family: var(--md-text-font-family), sans-serif;
}
.md-typeset svg.nj-fig--range { min-width: 30rem; max-width: 40rem; }
.md-typeset svg.nj-fig--cb { max-width: 30rem; }
.nj-fig { --fg-line: color-mix(in srgb, currentColor 55%, var(--md-default-bg-color)); }
.nj-fig .fg-cell { fill: color-mix(in srgb, var(--md-typeset-a-color) 14%, var(--md-default-bg-color)); stroke: var(--fg-line); stroke-width: 1.5; }
.nj-fig .fg-cell--end { fill: none; stroke-dasharray: 4 3; }
.nj-fig .fg-pill { fill: color-mix(in srgb, var(--md-typeset-a-color) 10%, var(--md-default-bg-color)); stroke: var(--md-typeset-a-color); stroke-width: 1.5; }
.nj-fig .fg-pilltext { fill: var(--md-typeset-a-color); font-family: var(--md-code-font-family), monospace; font-size: 14px; font-weight: 500; text-anchor: middle; }
.nj-fig .fg-arrow { stroke: var(--fg-line); stroke-width: 1.5; fill: none; }
.nj-fig .fg-arrow--dash { stroke-dasharray: 3 3; }
.nj-fig .fg-head { fill: var(--fg-line); }
.nj-fig .fg-note { fill: currentColor; fill-opacity: .7; font-size: 13.5px; text-anchor: middle; }
.nj-fig .fg-note--r { text-anchor: end; }
.nj-fig .fg-note--l { text-anchor: start; }
.nj-fig .fg-h { fill: currentColor; fill-opacity: .7; font-size: 15px; font-weight: 600; }
.nj-fig .fg-code { fill: currentColor; font-family: var(--md-code-font-family), monospace; font-size: 16px; white-space: pre; }
.nj-fig .fg-leg { font-size: 15px; }
.nj-fig { --cb-os: #4eb05a; --cb-oe: #1a6b34; --cb-k: #e5534b; --cb-as: #f0c000; --cb-ae: #b07d00; --cb-v: #1f6fd1; }
[data-md-color-scheme="slate"] .nj-fig { --cb-os: #7ee787; --cb-oe: #2ea043; --cb-k: #ff7b72; --cb-as: #f2cc60; --cb-ae: #c69026; --cb-v: #58a6ff; }
.nj-fig .cb-os { fill: var(--cb-os); }
.nj-fig .cb-oe { fill: var(--cb-oe); }
.nj-fig .cb-k { fill: var(--cb-k); }
.nj-fig .cb-as { fill: var(--cb-as); }
.nj-fig .cb-ae { fill: var(--cb-ae); }
.nj-fig .cb-v { fill: var(--cb-v); }
.md-typeset img[src*="json-light"], .md-typeset img[src*="json-dark"] {
border-radius: 8px;
}
.md-typeset img {
max-width: 100%;
height: auto;
}
/* home page: content uses the same column as every other page */
body:has(.nj-hero) .md-content {
max-width: none;
margin: 0;
}
.md-typeset .grid.cards {
width: auto;
}
/* 404 page (overrides/404.html) */
.nj-404 {
max-width: 30rem;
padding: 2rem 0 3rem;
}
.nj-404__code {
margin: 0 0 .4rem;
color: var(--nj-blue);
font-family: var(--md-code-font-family);
font-size: .8rem;
font-weight: 500;
}
.md-typeset .nj-404 h1 {
margin-bottom: .8rem;
}
.nj-404 .md-button {
margin: .4rem .4rem 0 0;
}
/* touch targets and phones */
@media screen and (max-width: 59.984375em) {
.md-nav__link {
padding-block: .2rem;
}
}
@media screen and (max-width: 29.984375em) {
.md-typeset h1 {
font-size: 1.7em;
}
.md-typeset .highlight pre > code {
font-size: .68rem;
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><g font-family='Menlo,monospace' font-size='26' font-weight='600' fill='#ffffff' fill-opacity='0.10'><text x='14' y='34'>{</text><text x='92' y='22'>]</text><text x='136' y='60'>:</text><text x='52' y='82'>[</text><text x='118' y='112'>,</text><text x='20' y='140'>}</text><text x='78' y='150'>:</text><text x='140' y='158'>{</text></g></svg>

Before

Width:  |  Height:  |  Size: 408 B

+5 -5
View File
@@ -124,20 +124,20 @@ For all the macros, the first parameter is the name of the class/struct. The `DE
```mermaid
flowchart TD
A["choosing a NLOHMANN_DEFINE_* macro"] --> B{{"adding fields to a base class?"}}
A["choosing a NLOHMANN_DEFINE_* macro"] --> B{"adding fields to a base class?"}
B -->|"yes"| C["...DERIVED_TYPE..."]
B -->|"no"| D["...TYPE..."]
C --> E{{"need access to private members?"}}
C --> E{"need access to private members?"}
D --> E
E -->|"yes"| F["...INTRUSIVE... (used inside the class)"]
E -->|"no"| G["...NON_INTRUSIVE... (used in the namespace)"]
F --> H{{"only serializing, never parsing back?"}}
F --> H{"only serializing, never parsing back?"}
G --> H
H -->|"yes"| I["...ONLY_SERIALIZE"]
H -->|"no"| J{{"allow missing keys when parsing?"}}
H -->|"no"| J{"allow missing keys when parsing?"}
J -->|"yes"| K["...WITH_DEFAULT"]
J -->|"no"| L["plain (missing keys throw)"]
I --> M{{"need custom JSON key names?"}}
I --> M{"need custom JSON key names?"}
K --> M
L --> M
M -->|"yes"| N["...WITH_NAMES"]
@@ -14,11 +14,11 @@ the value, or `end()` if it is not found).
```mermaid
flowchart TD
A["accessing a value"] --> B{{"must it exist?"}}
A["accessing a value"] --> B{"must it exist?"}
B -->|"yes, missing is an error"| C["at() -- throws"]
B -->|"yes, but checking is my job"| D["operator[] -- unchecked"]
B -->|"no, a fallback is fine"| E["value() -- default value"]
A --> F{{"just testing first?"}}
A --> F{"just testing first?"}
F -->|"yes"| G["contains() / find()"]
```
+2 -6
View File
@@ -9,9 +9,7 @@ As for other containers, [`begin()`](../api/basic_json/begin.md) returns an iter
placeholder and cannot be dereferenced. In case of null values, empty arrays, or empty objects, `begin()` will return
`end()`.
<div class="nj-fig-wrap" markdown>
--8<-- "images/range-begin-end.svg"
</div>
![Illustration from cppreference.com](../images/range-begin-end.svg)
### Iteration order for objects
@@ -114,9 +112,7 @@ for (auto& [key, val] : j_object.items())
[`rbegin()`](../api/basic_json/rbegin.md) and [`rend()`](../api/basic_json/rend.md) return iterators in the reverse
sequence.
<div class="nj-fig-wrap" markdown>
--8<-- "images/range-rbegin-rend.svg"
</div>
![Illustration from cppreference.com](../images/range-rbegin-rend.svg)
??? example "Example: reverse iteration with `rbegin()` and `rend()`"
@@ -27,9 +27,7 @@ JSON defines the syntax of numbers as follows:
The following railroad diagram from [json.org](https://json.org) visualizes the number syntax:
<div class="railroad-wrap" markdown>
--8<-- "images/json_syntax_number.svg"
</div>
![Syntax for JSON numbers](../../images/json_syntax_number.png)
### Number interoperability
@@ -71,9 +69,9 @@ otherwise, it uses unsigned integer storage.
```mermaid
flowchart TD
A["number literal"] --> B{{"has a fraction (.) or exponent (e/E)?"}}
A["number literal"] --> B{"has a fraction (.) or exponent (e/E)?"}
B -->|"yes"| F["number_float_t"]
B -->|"no"| C{{"has a leading minus sign?"}}
B -->|"no"| C{"has a leading minus sign?"}
C -->|"yes"| D["try number_integer_t"]
C -->|"no"| E["try number_unsigned_t"]
D -->|"overflow"| F
Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

@@ -1,39 +0,0 @@
<svg class="nj-fig nj-fig--cb" viewBox="0 0 480 262" role="img" aria-labelledby="fig-cb-title" xmlns="http://www.w3.org/2000/svg">
<title id="fig-cb-title">Example of the parse events a callback sees for the J&#8203;SON text {&quot;name&quot;: &quot;Berlin&quot;, &quot;location&quot;: [52.519444, 13.406667]}: object_start after the opening brace, key after each key, array_start after the opening bracket, value after each value, array_end after the closing bracket, and object_end after the closing brace. Every array and object also produces a value event when it is complete.</title>
<text class="fg-h" x="16" y="28">J&#8203;SON to parse</text>
<text class="fg-h" x="320" y="28">callback events</text>
<text class="fg-code" x="16" y="74" xml:space="preserve">{</text>
<circle class="cb-os" cx="35.6" cy="69" r="5.5"/>
<text class="fg-code" x="35.2" y="102" xml:space="preserve">&quot;name&quot;</text>
<circle class="cb-k" cx="102.8" cy="97" r="5.5"/>
<text class="fg-code" x="112.8" y="102" xml:space="preserve">: &quot;Berlin&quot;</text>
<circle class="cb-v" cx="218.8" cy="97" r="5.5"/>
<text class="fg-code" x="228.8" y="102" xml:space="preserve">,</text>
<text class="fg-code" x="35.2" y="130" xml:space="preserve">&quot;location&quot;</text>
<circle class="cb-k" cx="141.2" cy="125" r="5.5"/>
<text class="fg-code" x="151.2" y="130" xml:space="preserve">: [</text>
<circle class="cb-as" cx="190" cy="125" r="5.5"/>
<text class="fg-code" x="54.4" y="158" xml:space="preserve">52.519444</text>
<circle class="cb-v" cx="150.8" cy="153" r="5.5"/>
<text class="fg-code" x="160.8" y="158" xml:space="preserve">,</text>
<text class="fg-code" x="54.4" y="186" xml:space="preserve">13.406667</text>
<circle class="cb-v" cx="150.8" cy="181" r="5.5"/>
<text class="fg-code" x="35.2" y="214" xml:space="preserve">]</text>
<circle class="cb-ae" cx="54.8" cy="209" r="5.5"/>
<circle class="cb-v" cx="74.8" cy="209" r="5.5"/>
<text class="fg-code" x="16" y="242" xml:space="preserve">}</text>
<circle class="cb-oe" cx="35.6" cy="237" r="5.5"/>
<circle class="cb-v" cx="55.6" cy="237" r="5.5"/>
<circle class="cb-os" cx="327" cy="69" r="6"/>
<text class="fg-code fg-leg" x="344" y="74">object_start</text>
<circle class="cb-k" cx="327" cy="99" r="6"/>
<text class="fg-code fg-leg" x="344" y="104">key</text>
<circle class="cb-oe" cx="327" cy="129" r="6"/>
<text class="fg-code fg-leg" x="344" y="134">object_end</text>
<circle class="cb-as" cx="327" cy="159" r="6"/>
<text class="fg-code fg-leg" x="344" y="164">array_start</text>
<circle class="cb-ae" cx="327" cy="189" r="6"/>
<text class="fg-code fg-leg" x="344" y="194">array_end</text>
<circle class="cb-v" cx="327" cy="219" r="6"/>
<text class="fg-code fg-leg" x="344" y="224">value</text>
</svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1200 675" width="1200" height="675" role="img"><title>JSON for Modern C++: feature slideshow (animated, 20 slides, 15 s each)</title><style>.bg{fill:#fff} .tx{fill:#0f1419} .tx2{fill:#3d444f} .tx3{fill:#8a919c} .com{fill:#646d7a}.bl{fill:#1747a6} .blk{fill:#1747a6} .kw{fill:#1747a6} .ty{fill:#0d6e72} .fn{fill:#6b3fb8} .str{fill:#23793a} .num{fill:#b04a07} .pp{fill:#a3247a}.panel{fill:#f5f7fa;stroke:#e3e6eb;stroke-width:1} .rule{stroke:#e3e6eb;stroke-width:1}.wm-a{fill:#0f1419} .wm-s{fill:#1747a6}.on-a,.on-tx{fill:#fff} .red{fill:#d93a3f} .on-panel{fill:#fff;fill-opacity:.08;stroke:#fff;stroke-opacity:.16} .on-s{fill:#bcd3fb} .w70{fill:#fff;fill-opacity:.7} .on-tx2{fill:#dbe6fb} .chip{fill:#fff;fill-opacity:.14}.pat{fill:#fff;fill-opacity:.09}.h1{stop-color:#1d55c4} .h2{stop-color:#123a8a}.seg{fill:#e3e6eb} .seg-on{fill:#1747a6}.seg.on-band{fill:#fff;fill-opacity:.25} .seg-on.on-band{fill:#fff;fill-opacity:1}.bg{fill:#0f1218} .tx,.wm-a{fill:#f2f4f7} .tx2{fill:#c3c8d1} .tx3{fill:#7b828e} .com{fill:#8b929e}.bl,.blk,.wm-s,.seg-on,.kw{fill:#7aa7f5} .ty{fill:#4fd1c5} .fn{fill:#c4a3ff} .str{fill:#7ed491} .num{fill:#f5a65b} .pp{fill:#f48fc8}.panel{fill:#171b23;stroke:#272c36} .rule{stroke:#272c36} .seg{fill:#272c36}.red{fill:#c8383d} .h1{stop-color:#163f93} .h2{stop-color:#0c2556}.s{opacity:0;animation:show 300s linear infinite backwards}.fill{transform-box:fill-box;transform-origin:left;animation:fill 300s linear infinite backwards;animation-delay:inherit}.s0{animation-name:show0} .sl{animation-name:showl}@keyframes show{0%{opacity:0;animation-timing-function:cubic-bezier(.2,.6,.2,1)} 0.2000%{opacity:1} 5.2000%{opacity:1} 5.2100%{opacity:0} 100%{opacity:0}}@keyframes show0{0%{opacity:1} 5.2000%{opacity:1} 5.2100%{opacity:0} 100%{opacity:0}}@keyframes showl{0%{opacity:0;animation-timing-function:cubic-bezier(.2,.6,.2,1)} 0.2000%{opacity:1} 5.0000%{opacity:1} 5.2000%{opacity:0} 100%{opacity:0}}@keyframes fill{0%{transform:scaleX(0)} 5.0000%{transform:scaleX(1)} 100%{transform:scaleX(1)}}@media (prefers-reduced-motion:reduce){.s,.fill{animation:none} .s:first-of-type{opacity:1}}</style><defs><path id="g0" d="M233 0 46 730H148L269 238Q281 191 289 148Q298 105 302 81Q306 105 315 148Q324 191 335 239L452 730H554L366 0H233Z"/><path id="g1" d="M97 0V730H522V641H195V424H487V337H195V89H522V0H97Z"/><path id="g2" d="M89 0V730H315Q382 730 432 703Q483 677 510 629Q538 582 538 518Q538 445 501 391Q463 338 401 316L548 0H439L300 305H187V0H89ZM187 393H315Q370 393 403 427Q436 461 436 517Q436 574 403 608Q370 642 315 642H187V393Z"/><path id="g3" d="M304 -10Q233 -10 179 15Q126 41 97 88Q68 136 68 202H167Q167 144 204 111Q242 78 304 78Q364 78 399 112Q435 146 435 201Q435 243 412 274Q390 306 347 317L239 346Q168 366 126 419Q85 471 85 543Q85 602 112 646Q138 691 186 715Q234 740 297 740Q361 740 409 715Q457 691 485 647Q512 603 512 545H414Q414 593 381 623Q348 653 297 653Q245 653 214 623Q182 593 182 545Q182 507 203 480Q224 453 262 442L373 412Q448 392 490 336Q532 279 532 201Q532 137 504 89Q476 42 424 16Q373 -10 304 -10Z"/><path id="g4" d="M101 0V90H250V640H101V730H499V640H350V90H499V0H101Z"/><path id="g5" d="M300 -10Q200 -10 142 48Q84 106 84 212V518Q84 625 142 682Q200 740 300 740Q400 740 458 682Q516 625 516 519V212Q516 106 458 48Q400 -10 300 -10ZM300 78Q357 78 387 110Q417 142 417 202V528Q417 588 387 620Q357 652 300 652Q244 652 213 620Q183 588 183 528V202Q183 142 213 110Q244 78 300 78Z"/><path id="g6" d="M85 0V730H214L432 114Q429 141 427 179Q424 217 422 259Q421 301 421 337V730H515V0H386L170 616Q172 591 174 553Q176 516 178 474Q179 433 179 396V0H85Z"/><path id="g8" d="M290 -10Q187 -10 127 46Q67 103 67 200H166Q166 143 200 110Q233 78 291 78Q348 78 382 110Q415 143 415 200V251Q415 308 382 341Q349 375 291 375H209V458L385 641H89V730H496V640L291 428L293 462Q395 462 455 405Q515 349 515 251V200Q515 103 454 46Q394 -10 290 -10Z"/><path id="g9" d="M300 -10Q262 -10 238 13Q215 36 215 72Q215 111 238 134Q262 158 300 158Q338 158 362 134Q385 111 385 72Q385 36 362 13Q338 -10 300 -10Z"/><path id="ga" d="M88 0V90H284V641L88 495V607L254 730H383V90H543V0H88Z"/><path id="gb" d="M300 -10Q232 -10 182 17Q132 43 104 92Q77 141 77 206V524Q77 590 104 638Q132 687 182 713Q232 740 300 740Q369 740 419 713Q468 687 496 638Q523 590 523 524V206Q523 141 496 92Q468 43 418 17Q368 -10 300 -10ZM300 75Q358 75 393 111Q428 148 428 206V524Q428 582 393 619Q358 655 300 655Q242 655 207 619Q172 582 172 524V206Q172 148 207 111Q242 75 300 75ZM300 309Q272 309 255 324Q238 339 238 368Q238 396 255 411Q272 425 300 425Q328 425 345 411Q362 396 362 368Q362 339 345 324Q328 309 300 309Z"/><path id="gc" d="M180 688V0H134V1056H180V816H168Q204 944 310 1008Q417 1072 544 1072Q659 1072 746 1024Q834 975 884 889Q934 803 934 688V0H888V688Q888 840 792 933Q696 1026 544 1026Q440 1026 358 982Q275 939 228 862Q180 786 180 688Z"/><path id="gd" d="M180 1490V0H134V1490H180Z"/><path id="ge" d="M530 -16Q394 -16 289 54Q184 125 125 248Q66 371 6Line truncated

Before

Width:  |  Height:  |  Size: 395 KiB

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1200 675" width="1200" height="675" role="img"><title>JSON for Modern C++: feature slideshow (animated, 20 slides, 15 s each)</title><style>.bg{fill:#fff} .tx{fill:#0f1419} .tx2{fill:#3d444f} .tx3{fill:#8a919c} .com{fill:#646d7a}.bl{fill:#1747a6} .blk{fill:#1747a6} .kw{fill:#1747a6} .ty{fill:#0d6e72} .fn{fill:#6b3fb8} .str{fill:#23793a} .num{fill:#b04a07} .pp{fill:#a3247a}.panel{fill:#f5f7fa;stroke:#e3e6eb;stroke-width:1} .rule{stroke:#e3e6eb;stroke-width:1}.wm-a{fill:#0f1419} .wm-s{fill:#1747a6}.on-a,.on-tx{fill:#fff} .red{fill:#d93a3f} .on-panel{fill:#fff;fill-opacity:.08;stroke:#fff;stroke-opacity:.16} .on-s{fill:#bcd3fb} .w70{fill:#fff;fill-opacity:.7} .on-tx2{fill:#dbe6fb} .chip{fill:#fff;fill-opacity:.14}.pat{fill:#fff;fill-opacity:.09}.h1{stop-color:#1d55c4} .h2{stop-color:#123a8a}.seg{fill:#e3e6eb} .seg-on{fill:#1747a6}.seg.on-band{fill:#fff;fill-opacity:.25} .seg-on.on-band{fill:#fff;fill-opacity:1}.s{opacity:0;animation:show 300s linear infinite backwards}.fill{transform-box:fill-box;transform-origin:left;animation:fill 300s linear infinite backwards;animation-delay:inherit}.s0{animation-name:show0} .sl{animation-name:showl}@keyframes show{0%{opacity:0;animation-timing-function:cubic-bezier(.2,.6,.2,1)} 0.2000%{opacity:1} 5.2000%{opacity:1} 5.2100%{opacity:0} 100%{opacity:0}}@keyframes show0{0%{opacity:1} 5.2000%{opacity:1} 5.2100%{opacity:0} 100%{opacity:0}}@keyframes showl{0%{opacity:0;animation-timing-function:cubic-bezier(.2,.6,.2,1)} 0.2000%{opacity:1} 5.0000%{opacity:1} 5.2000%{opacity:0} 100%{opacity:0}}@keyframes fill{0%{transform:scaleX(0)} 5.0000%{transform:scaleX(1)} 100%{transform:scaleX(1)}}@media (prefers-reduced-motion:reduce){.s,.fill{animation:none} .s:first-of-type{opacity:1}}</style><defs><path id="g0" d="M233 0 46 730H148L269 238Q281 191 289 148Q298 105 302 81Q306 105 315 148Q324 191 335 239L452 730H554L366 0H233Z"/><path id="g1" d="M97 0V730H522V641H195V424H487V337H195V89H522V0H97Z"/><path id="g2" d="M89 0V730H315Q382 730 432 703Q483 677 510 629Q538 582 538 518Q538 445 501 391Q463 338 401 316L548 0H439L300 305H187V0H89ZM187 393H315Q370 393 403 427Q436 461 436 517Q436 574 403 608Q370 642 315 642H187V393Z"/><path id="g3" d="M304 -10Q233 -10 179 15Q126 41 97 88Q68 136 68 202H167Q167 144 204 111Q242 78 304 78Q364 78 399 112Q435 146 435 201Q435 243 412 274Q390 306 347 317L239 346Q168 366 126 419Q85 471 85 543Q85 602 112 646Q138 691 186 715Q234 740 297 740Q361 740 409 715Q457 691 485 647Q512 603 512 545H414Q414 593 381 623Q348 653 297 653Q245 653 214 623Q182 593 182 545Q182 507 203 480Q224 453 262 442L373 412Q448 392 490 336Q532 279 532 201Q532 137 504 89Q476 42 424 16Q373 -10 304 -10Z"/><path id="g4" d="M101 0V90H250V640H101V730H499V640H350V90H499V0H101Z"/><path id="g5" d="M300 -10Q200 -10 142 48Q84 106 84 212V518Q84 625 142 682Q200 740 300 740Q400 740 458 682Q516 625 516 519V212Q516 106 458 48Q400 -10 300 -10ZM300 78Q357 78 387 110Q417 142 417 202V528Q417 588 387 620Q357 652 300 652Q244 652 213 620Q183 588 183 528V202Q183 142 213 110Q244 78 300 78Z"/><path id="g6" d="M85 0V730H214L432 114Q429 141 427 179Q424 217 422 259Q421 301 421 337V730H515V0H386L170 616Q172 591 174 553Q176 516 178 474Q179 433 179 396V0H85Z"/><path id="g8" d="M290 -10Q187 -10 127 46Q67 103 67 200H166Q166 143 200 110Q233 78 291 78Q348 78 382 110Q415 143 415 200V251Q415 308 382 341Q349 375 291 375H209V458L385 641H89V730H496V640L291 428L293 462Q395 462 455 405Q515 349 515 251V200Q515 103 454 46Q394 -10 290 -10Z"/><path id="g9" d="M300 -10Q262 -10 238 13Q215 36 215 72Q215 111 238 134Q262 158 300 158Q338 158 362 134Q385 111 385 72Q385 36 362 13Q338 -10 300 -10Z"/><path id="ga" d="M88 0V90H284V641L88 495V607L254 730H383V90H543V0H88Z"/><path id="gb" d="M300 -10Q232 -10 182 17Q132 43 104 92Q77 141 77 206V524Q77 590 104 638Q132 687 182 713Q232 740 300 740Q369 740 419 713Q468 687 496 638Q523 590 523 524V206Q523 141 496 92Q468 43 418 17Q368 -10 300 -10ZM300 75Q358 75 393 111Q428 148 428 206V524Q428 582 393 619Q358 655 300 655Q242 655 207 619Q172 582 172 524V206Q172 148 207 111Q242 75 300 75ZM300 309Q272 309 255 324Q238 339 238 368Q238 396 255 411Q272 425 300 425Q328 425 345 411Q362 396 362 368Q362 339 345 324Q328 309 300 309Z"/><path id="gc" d="M180 688V0H134V1056H180V816H168Q204 944 310 1008Q417 1072 544 1072Q659 1072 746 1024Q834 975 884 889Q934 803 934 688V0H888V688Q888 840 792 933Q696 1026 544 1026Q440 1026 358 982Q275 939 228 862Q180 786 180 688Z"/><path id="gd" d="M180 1490V0H134V1490H180Z"/><path id="ge" d="M530 -16Q394 -16 289 54Q184 125 125 248Q66 371 66 528Q66 686 125 808Q184 931 289 1002Q394 1072 530 1072Q666 1072 770 1002Q874 931 933 808Q992 686 992 528Q992 371 933 248Q874 125 770 54Q666 -16 530 -16ZM530 30Q652 30 746 94Q840 157 893 270Q946 382 946 528Q946 674 893 786Q840 899 746 962Q652 1026 530 1026Q408 1026 314 962Q219 899 166 786Q112 674 112 528Q112 382 166 270Q219 157 314 94Q408 30 530 30Z"/><path id="gf" d="MLine truncated

Before

Width:  |  Height:  |  Size: 394 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

@@ -1,66 +0,0 @@
<svg class="railroad" viewBox="0 0 860 214" role="img" aria-labelledby="rr-title" xmlns="http://www.w3.org/2000/svg">
<title id="rr-title">Railroad diagram of the J&#8203;SON number syntax: optional minus, then 0 or a digit from 1 to 9 followed by digits, then an optional fraction (a dot and digits) and an optional exponent (e or E, an optional sign, and digits).</title>
<g class="rr-track" fill="none" stroke="currentColor" stroke-opacity=".6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M8 44 L850 44"/>
<path d="M24 44 L31 44 Q38 44 38 51 L38 82 Q38 92 48 92 L59 92"/>
<path d="M85 92 L96 92 Q106 92 106 82 L106 51 Q106 44 113 44 L120 44"/>
<path d="M120 44 L127 44 Q134 44 134 51 L134 82 Q134 92 144 92 L160 92"/>
<path d="M242 92 L370 92 Q380 92 380 82 L380 52 Q380 44 388 44 L396 44"/>
<path d="M242 92 L250 92 Q258 92 258 100 L258 126 Q258 136 268 136 L278 136"/>
<path d="M332 136 L340 136 Q348 136 348 128 L348 92"/>
<path d="M340 136 L340 160 Q340 170 330 170 L280 170 Q270 170 270 160 L270 136"/>
<path d="M396 44 L403 44 Q410 44 410 51 L410 84.5 Q410 92 417.5 92 L425 92"/>
<path d="M451 92 L462 92"/>
<path d="M524 92 L524 116 Q524 126 514 126 L466 126 Q456 126 456 116 L456 92"/>
<path d="M516 92 L523 92 Q530 92 530 85 L530 52 Q530 44 538 44 L546 44"/>
<path d="M546 44 L553 44 Q560 44 560 51 L560 83.5 Q560 92 568.5 92 L577 92"/>
<path d="M546 44 L553 44 Q560 44 560 51 L560 127.5 Q560 136 568.5 136 L577 136"/>
<path d="M603 92 L728 92"/>
<path d="M603 136 L611.5 136 Q620 136 620 127.5 L620 92"/>
<path d="M640 92 L647 92 Q654 92 654 99 L654 128 Q654 136 662 136 L670 136"/>
<path d="M640 92 L647 92 Q654 92 654 99 L654 172 Q654 180 662 180 L670 180"/>
<path d="M696 136 L704 136 Q712 136 712 128 L712 92"/>
<path d="M696 180 L704 180 Q712 180 712 172 L712 92"/>
<path d="M728 92 L746 92"/>
<path d="M808 92 L808 116 Q808 126 798 126 L748 126 Q738 126 738 116 L738 92"/>
<path d="M800 92 L808 92 Q816 92 816 84 L816 52 Q816 44 824 44 L832 44"/>
</g>
<polygon class="rr-arrow" points="2,38 12,44 2,50"/>
<polygon class="rr-arrow" points="846,38 858,44 846,50"/>
<circle class="rr-bg" cx="72" cy="92" r="13"/>
<circle class="rr-t" cx="72" cy="92" r="13"/>
<circle class="rr-bg" cx="257" cy="44" r="13"/>
<circle class="rr-t" cx="257" cy="44" r="13"/>
<rect class="rr-bg" x="160" y="78" width="82" height="28" rx="6"/>
<rect class="rr-n" x="160" y="78" width="82" height="28" rx="6"/>
<rect class="rr-bg" x="278" y="122" width="54" height="28" rx="6"/>
<rect class="rr-n" x="278" y="122" width="54" height="28" rx="6"/>
<circle class="rr-bg" cx="438" cy="92" r="13"/>
<circle class="rr-t" cx="438" cy="92" r="13"/>
<rect class="rr-bg" x="462" y="78" width="54" height="28" rx="6"/>
<rect class="rr-n" x="462" y="78" width="54" height="28" rx="6"/>
<circle class="rr-bg" cx="590" cy="92" r="13"/>
<circle class="rr-t" cx="590" cy="92" r="13"/>
<circle class="rr-bg" cx="590" cy="136" r="13"/>
<circle class="rr-t" cx="590" cy="136" r="13"/>
<circle class="rr-bg" cx="683" cy="136" r="13"/>
<circle class="rr-t" cx="683" cy="136" r="13"/>
<circle class="rr-bg" cx="683" cy="180" r="13"/>
<circle class="rr-t" cx="683" cy="180" r="13"/>
<rect class="rr-bg" x="746" y="78" width="54" height="28" rx="6"/>
<rect class="rr-n" x="746" y="78" width="54" height="28" rx="6"/>
<text class="rr-c" x="72" y="97">&#8722;</text>
<text class="rr-c" x="257" y="49">0</text>
<text class="rr-b" x="201" y="97">digit 1&#8211;9</text>
<text class="rr-b" x="305" y="141">digit</text>
<text class="rr-c" x="438" y="97">.</text>
<text class="rr-b" x="489" y="97">digit</text>
<text class="rr-c" x="590" y="97">e</text>
<text class="rr-c" x="590" y="141">E</text>
<text class="rr-c" x="683" y="141">&#8722;</text>
<text class="rr-c" x="683" y="185">+</text>
<text class="rr-b" x="773" y="97">digit</text>
<text class="rr-l rr-name" x="24" y="32">number</text>
<text class="rr-l" x="420" y="32">fraction</text>
<text class="rr-l" x="580" y="32">exponent</text>
</svg>

Before

Width:  |  Height:  |  Size: 3.9 KiB

+446 -27
View File
@@ -1,28 +1,447 @@
<svg class="nj-fig nj-fig--range" viewBox="0 0 600 150" role="img" aria-labelledby="fig-be-title" xmlns="http://www.w3.org/2000/svg">
<title id="fig-be-title">A row of fifteen elements followed by a dashed past-the-last position. begin() points to the first element and end() points to the past-the-last position.</title>
<defs><marker id="fig-be-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path class="fg-head" d="M0 1 L10 5 L0 9 z"/></marker></defs>
<rect class="fg-cell" x="30" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="64" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="98" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="132" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="166" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="200" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="234" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="268" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="302" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="336" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="370" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="404" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="438" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="472" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="506" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell fg-cell--end" x="540" y="62" width="30" height="30" rx="5"/>
<rect class="fg-pill" x="15" y="8" width="60" height="24" rx="6"/>
<text class="fg-pilltext" x="45" y="24">begin</text>
<line class="fg-arrow" x1="45" y1="32" x2="45" y2="60" marker-end="url(#fig-be-arrow)"/>
<rect class="fg-pill" x="531" y="8" width="48" height="24" rx="6"/>
<text class="fg-pilltext" x="555" y="24">end</text>
<line class="fg-arrow" x1="555" y1="32" x2="555" y2="60" marker-end="url(#fig-be-arrow)"/>
<text class="fg-note fg-note--r" x="537" y="132">Past-the-last element</text>
<line class="fg-arrow" x1="555" y1="124" x2="555" y2="96" marker-end="url(#fig-be-arrow)"/>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="545.89282"
height="156.9409"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="range-begin-end.svg">
<style type="text/css">
@media (prefers-color-scheme: dark) {
[style*="stroke:#000000"] { stroke: #e6e6e6 !important; }
[style*="fill:#000000"] { fill: #e6e6e6 !important; }
[style*="fill:#ffffff"] { fill: #1e1e1e !important; }
[style*="fill:#0000ff"] { fill: #7aa2ff !important; }
marker path { fill: #e6e6e6 !important; stroke: #e6e6e6 !important; }
stop[style*="#c1b1b1"] { stop-color: #5a5a5a !important; }
stop[style*="#b6b6b6"] { stop-color: #4a4a4a !important; }
stop[style*="#e3e3e3"] { stop-color: #2a2a2a !important; }
}
</style>
<defs
id="defs4">
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend"
style="overflow:visible">
<path
id="path6525"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.4,0,0,-0.4,-4,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lend"
style="overflow:visible">
<path
id="path6519"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
inkscape:connector-curvature="0" />
</marker>
<linearGradient
id="linearGradient5488">
<stop
style="stop-color:#c1b1b1;stop-opacity:1;"
offset="0"
id="stop5490" />
<stop
style="stop-color:#e3e3e3;stop-opacity:0;"
offset="1"
id="stop5492" />
</linearGradient>
<linearGradient
id="linearGradient4040">
<stop
style="stop-color:#b6b6b6;stop-opacity:1;"
offset="0"
id="stop4042" />
<stop
style="stop-color:#e3e3e3;stop-opacity:0;"
offset="1"
id="stop4044" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5488"
id="linearGradient6284"
gradientUnits="userSpaceOnUse"
x1="81"
y1="1001.3622"
x2="81"
y2="971.36218" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5488-2"
id="linearGradient6284-3"
gradientUnits="userSpaceOnUse"
x1="81"
y1="1001.3622"
x2="81"
y2="971.36218" />
<linearGradient
id="linearGradient5488-2">
<stop
style="stop-color:#c1b1b1;stop-opacity:1;"
offset="0"
id="stop5490-5" />
<stop
style="stop-color:#e3e3e3;stop-opacity:0;"
offset="1"
id="stop5492-8" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5488-0"
id="linearGradient6284-5"
gradientUnits="userSpaceOnUse"
x1="81"
y1="1001.3622"
x2="81"
y2="971.36218" />
<linearGradient
id="linearGradient5488-0">
<stop
style="stop-color:#c1b1b1;stop-opacity:1;"
offset="0"
id="stop5490-1" />
<stop
style="stop-color:#e3e3e3;stop-opacity:0;"
offset="1"
id="stop5492-3" />
</linearGradient>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend-8"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path6525-3"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5488"
id="linearGradient9026"
gradientUnits="userSpaceOnUse"
x1="81"
y1="1001.3622"
x2="81"
y2="971.36218" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4"
inkscape:cx="332.80082"
inkscape:cy="165.04542"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1680"
inkscape:window-height="993"
inkscape:window-x="0"
inkscape:window-y="34"
inkscape:window-maximized="1"
inkscape:snap-bbox="true"
inkscape:object-nodes="true"
inkscape:snap-smooth-nodes="false"
inkscape:bbox-nodes="false"
inkscape:snap-bbox-edge-midpoints="false"
inkscape:object-paths="true"
fit-margin-top="10"
fit-margin-right="10"
fit-margin-bottom="10"
fit-margin-left="10" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-26.107143,-893.4807)">
<g
id="g7184">
<path
inkscape:tile-y0="219"
inkscape:tile-x0="51"
inkscape:tile-h="30.00002"
inkscape:tile-w="29.999997"
inkscape:tile-cy="234.00001"
inkscape:tile-cx="65.999998"
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="path3006"
d="m 80.999997,1001.3622 -29.999997,0 0,-30.00002 29.999997,0 z"
style="fill:url(#linearGradient9026);fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<use
height="300"
width="600"
style="fill:none"
id="use4010"
transform="translate(29.999997,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0" />
<use
id="use4012"
transform="translate(59.999994,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4014"
transform="translate(89.999991,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4016"
transform="translate(119.99999,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4018"
transform="translate(149.99998,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4020"
transform="translate(179.99998,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4022"
transform="translate(209.99998,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4024"
transform="translate(239.99998,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4026"
transform="translate(269.99997,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4028"
transform="translate(299.99997,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4030"
transform="translate(329.99997,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4032"
transform="translate(359.99996,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4034"
transform="translate(389.99996,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4036"
transform="translate(419.99996,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4038"
transform="translate(449.99995,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<path
inkscape:tile-y0="219"
inkscape:tile-x0="51"
inkscape:tile-h="30.00002"
inkscape:tile-w="29.999997"
inkscape:tile-cy="234.00001"
inkscape:tile-cx="65.999998"
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="path3006-3"
d="m 560.99995,1001.3622 -30,0 0,-30.00002 30,0 z"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 2;stroke-dashoffset:0;marker-start:none" />
</g>
<text
xml:space="preserve"
style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:55.00000119%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
x="392.94931"
y="1040.2404"
id="text6504"
sodipodi:linespacing="55.000001%"><tspan
sodipodi:role="line"
id="tspan6506"
x="392.94931"
y="1040.2404"
rotate="0 0 0 0 0 0 0 0 0.50000191 0 0 0 0 0 0 0 0 0 0 0 0 0"
dy="0"
dx="0"
style="font-size:12.80000019px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans">Past-the-last element</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow1Mend)"
d="m 531.84531,279.29187 c 14.75083,-11.1311 12.86656,-14.42379 15.15229,-29.29442"
id="path6510"
inkscape:connector-curvature="0"
transform="translate(0,752.36218)"
sodipodi:nodetypes="cc" />
<g
id="g7203"
transform="translate(-40,-18.482143)">
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 128.39286,947.36218 -51.785717,0 0,-21.51786 51.785717,0 z"
id="use6288-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
inkscape:tile-x0="51"
inkscape:tile-y0="219" />
<text
transform="translate(0,752.36218)"
sodipodi:linespacing="55.000001%"
id="text7176"
y="189.10715"
x="80.535713"
style="font-size:15px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:55.00000119%;letter-spacing:0px;word-spacing:0px;fill:#0000ff;fill-opacity:1;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans"
xml:space="preserve"><tspan
y="189.10715"
x="80.535713"
id="tspan7178"
sodipodi:role="line">begin</tspan></text>
</g>
<g
id="g7238"
transform="translate(20.035895,11.306019)">
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 538.39286,914.19254 -37.50001,0 0,-21.51786 37.50001,0 z"
id="use6288-9-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
inkscape:tile-x0="51"
inkscape:tile-y0="219" />
<text
sodipodi:linespacing="55.000001%"
id="text7176-3"
y="908.29968"
x="504.82141"
style="font-size:15px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:55.00000119%;letter-spacing:0px;word-spacing:0px;fill:#0000ff;fill-opacity:1;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans"
xml:space="preserve"><tspan
y="908.29968"
x="504.82141"
id="tspan7178-7"
sodipodi:role="line">end</tspan></text>
</g>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow1Mend)"
d="M 60.16588,176.51786 C 64.809921,191.17027 67.41717,201.02245 68.196339,219"
id="path7243"
inkscape:connector-curvature="0"
transform="translate(0,752.36218)"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow1Mend)"
d="m 540.40821,925.49856 c 2.63474,15.08647 5.72454,31.23577 6.06617,45.86362"
id="path7243-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because it is too large. Load diff

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 41 KiB

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-78 -1741 14091 2499" width="495" height="88" role="img" aria-label="nlohmann/json"><title>nlohmann/json</title><g transform="scale(1 -1)"><g fill="#ffffff"><path transform="translate(0.0 0)" d="M279.2 697.3V0H167.3V1104.2H276.1L276.4 850.4H255.4Q299 990.6 399.3 1055.2Q499.6 1119.9 626.3 1119.9Q739.7 1119.9 826.2 1072.4Q912.8 1024.9 962.1 935.2Q1011.4 845.5 1011.4 717.1V0H899.6V709.9Q899.6 851.3 818 933.9Q736.5 1016.6 601.8 1016.6Q510.1 1016.6 437 977.7Q363.8 938.9 321.5 867.2Q279.2 795.6 279.2 697.3Z"/><path transform="translate(1138.0 0)" d="M279.2 1490V0H167.3V1490H279.2Z"/><path transform="translate(1544.1 0)" d="M590.7 -23.1Q448.9 -23.1 339.6 50.3Q230.3 123.7 168.3 252.7Q106.3 381.6 106.3 548Q106.3 715.8 168.3 844.8Q230.3 973.8 339.6 1047.1Q448.9 1120.3 590.7 1120.3Q732.6 1120.3 841.7 1047.1Q950.9 973.8 1012.9 844.6Q1074.9 715.4 1074.9 548Q1074.9 381.6 1013.1 252.7Q951.3 123.7 842.1 50.3Q733 -23.1 590.7 -23.1ZM590.7 79.6Q705.6 79.6 788.9 141.9Q872.2 204.2 917.1 310.4Q962 416.7 962 548Q962 679.6 916.9 786Q871.8 892.4 788.5 955Q705.2 1017.6 590.7 1017.6Q476.7 1017.6 393.5 954.9Q310.3 892.2 265 786.1Q219.6 679.9 219.6 548Q219.6 416.6 264.8 310.4Q309.9 204.2 393.1 141.9Q476.2 79.6 590.7 79.6Z"/><path transform="translate(2684.1 0)" d="M279.2 697.3V0H167.3V1490H279.2V850.4H255.4Q299 990.6 399.3 1055.2Q499.6 1119.9 626.3 1119.9Q739.7 1119.9 826.4 1072.4Q913.2 1024.9 962.5 935.2Q1011.8 845.5 1011.8 717.1V0H899.6V709.9Q899.6 851.3 818 933.9Q736.5 1016.6 601.8 1016.6Q510.1 1016.6 437 977.7Q363.8 938.9 321.5 867.2Q279.2 795.6 279.2 697.3Z"/><path transform="translate(3822.2 0)" d="M167.3 0V1104.2H276.8L277.2 853.7H261.4Q285.1 945.7 337 1004.8Q388.9 1064 457.8 1093.1Q526.7 1122.2 599.7 1122.2Q722.9 1122.2 805.7 1046.7Q888.4 971.2 910.3 844.9H888.1Q907.9 930.7 959.4 992.8Q1011 1054.9 1085.3 1088.6Q1159.6 1122.2 1248.1 1122.2Q1349.1 1122.2 1427 1078Q1505 1033.7 1549.5 948.2Q1594 862.7 1594 738.2V0H1482.1V736.3Q1482.1 878 1408.3 948Q1334.4 1018.1 1221.8 1018.1Q1139.1 1018.1 1074.7 982.6Q1010.3 947 973.4 881.6Q936.6 816.3 936.6 726.7V0H824.7V745.3Q824.7 873 754.3 945.6Q683.9 1018.1 570.7 1018.1Q491.1 1018.1 424.8 981.1Q358.4 944 318.8 875.8Q279.2 807.7 279.2 713.4V0H167.3Z"/><path transform="translate(5542.2 0)" d="M560.3 -22.7Q425.4 -22.7 323.1 49.7Q220.8 122 163.6 251Q106.3 380.1 106.3 549.8Q106.3 718.7 164 847.4Q221.7 976 324.4 1047.9Q427.1 1119.9 561.9 1119.9Q657.4 1119.9 727.7 1085.2Q798.1 1050.6 845.2 994.3Q892.3 938.1 916.4 874.3H924.9V1104.2H1036.2V0H925.3V225.1H916.4Q891.1 161.2 843.7 104.4Q796.3 47.5 725.9 12.4Q655.4 -22.7 560.3 -22.7ZM574.9 79.9Q685.3 79.9 764.2 140.4Q843.1 200.8 885.1 307.1Q927.2 413.4 927.2 550.6Q927.2 687.8 885.1 793Q843.1 898.2 764.2 957.8Q685.3 1017.4 574.9 1017.4Q464.6 1017.4 384.9 957.2Q305.3 897.1 262.4 791.7Q219.6 686.2 219.6 550.6Q219.6 415 262.4 308.6Q305.3 202.3 384.9 141.1Q464.6 79.9 574.9 79.9Z"/><path transform="translate(6705.2 0)" d="M279.2 697.3V0H167.3V1104.2H276.1L276.4 850.4H255.4Q299 990.6 399.3 1055.2Q499.6 1119.9 626.3 1119.9Q739.7 1119.9 826.2 1072.4Q912.8 1024.9 962.1 935.2Q1011.4 845.5 1011.4 717.1V0H899.6V709.9Q899.6 851.3 818 933.9Q736.5 1016.6 601.8 1016.6Q510.1 1016.6 437 977.7Q363.8 938.9 321.5 867.2Q279.2 795.6 279.2 697.3Z"/><path transform="translate(7843.3 0)" d="M279.2 697.3V0H167.3V1104.2H276.1L276.4 850.4H255.4Q299 990.6 399.3 1055.2Q499.6 1119.9 626.3 1119.9Q739.7 1119.9 826.2 1072.4Q912.8 1024.9 962.1 935.2Q1011.4 845.5 1011.4 717.1V0H899.6V709.9Q899.6 851.3 818 933.9Q736.5 1016.6 601.8 1016.6Q510.1 1016.6 437 977.7Q363.8 938.9 321.5 867.2Q279.2 795.6 279.2 697.3Z"/><path transform="translate(9729.7 0)" d="M111.2 1104.2H459.2V-51.7Q459.4 -183.9 406.6 -264.4Q353.7 -344.9 255.5 -381.4Q157.2 -418 21.4 -418H-63.7V-152.7H-14.8Q55.3 -152.7 83.3 -125Q111.2 -97.4 111.2 -39.2V1104.2ZM285.1 1228.6Q208.7 1228.6 155.6 1278Q102.6 1327.5 102.6 1398.2Q102.6 1469.9 155.6 1518.8Q208.7 1567.8 285 1567.8Q361.5 1567.8 414.7 1518.9Q467.8 1470.1 467.8 1398.1Q467.8 1327.4 414.7 1278Q361.5 1228.6 285.1 1228.6Z"/><path transform="translate(10258.7 0)" d="M580.7 -22.1Q436.8 -22.1 326.1 19.4Q215.5 60.9 146.9 138.4Q78.2 215.9 61 323.7L388.3 363.3Q406.3 294.2 455.2 257.5Q504.1 220.8 589.7 220.8Q662.2 220.8 703.6 246.4Q745 271.9 745 313.9Q745 351.2 711.8 374.4Q678.7 397.6 610.3 410.8L416 447.9Q252.5 479.2 170.3 559.5Q88.1 639.9 88.1 767.7Q88.1 878 147.7 956.7Q207.4 1035.5 316.7 1077.7Q426.1 1119.9 575.6 1119.9Q717.4 1119.9 822.1 1080.8Q926.7 1041.7 989.4 969.8Q1052.1 897.9 1067.5 797.8L757.5 759.8Q745.3 815.8 699.4 848.2Q653.5 880.6 583.2 880.6Q518.8 880.6 476.8 855.3Q434.8 829.9 434.8 787.7Q434.8 753.9 462.5 729Q490.3 704.1 557.8 691L771.4 651.3Q937.2 620.2 1017.9 546.8Q1098.7 473.4 1098.7 352.5Q1098.7 239.1 1032.3 154.9Q965.9 70.8 849.2 24.3Q732.5 -22.1 580.7 -22.1Z"/><path transform="translate(11376.8 0)" d="M628.4 -21.6Q455.7 -21.6 329.7 49.8Q203.6 121.3 135.6 249.6Q67.7 378 67.7 548.9Q67.7 720.4 135.6 848.9Q203.6 977.3 329.7Line truncated

Before

Width:  |  Height:  |  Size: 5.8 KiB

+66 -8
View File
@@ -1,11 +1,63 @@
---
template: home.html
hide:
- toc
---
# JSON for Modern C++
![JSON for Modern C++](images/json.gif)
JSON for Modern C++ is a header-only C++11 library that turns JSON into a first-class C++ data type, using the operator
magic of modern C++ so that creating, reading, and modifying JSON values feels as natural as it does in languages like
Python. The whole library is available as a single header, `json.hpp`, with no dependencies, no subproject, and no
complex build system to set up; a companion header, `json_fwd.hpp`, provides forward declarations to keep compile times
down, `<nlohmann/json_literals.hpp>` holds the user-defined string literals on their own, and
`<nlohmann/json_view.hpp>` adds zero-copy views and editable documents. See
[header-only integration](integration/index.md) for details. It is heavily unit-tested with 100% code
coverage, checked with Valgrind and the Clang Sanitizers for memory leaks, and continuously fuzz-tested by Google
OSS-Fuzz.
## Quick start
Add the single header to your project and use the library like this:
```cpp
#include <iostream>
#include <nlohmann/json.hpp>
using nlohmann::json;
int main()
{
// parse a JSON string
json j = json::parse(R"({"happy": true, "pi": 3.141})");
// access and modify values
j["name"] = "Niels";
j["list"] = {1, 0, 2};
// read a value into a C++ variable and use it
double pi = j["pi"].get<double>();
std::cout << "2 * pi = " << 2 * pi << '\n';
// serialize with an indent of 4 spaces
std::cout << j.dump(4) << '\n';
}
```
Get the library by copying the single header [`json.hpp`](https://github.com/nlohmann/json/releases) from the
releases page into a directory `nlohmann` on your include path, or by installing it with a package manager:
```sh
brew install nlohmann-json # Homebrew
vcpkg install nlohmann-json # vcpkg
```
```cmake
find_package(nlohmann_json 3.12.0 REQUIRED)
target_link_libraries(myproject PRIVATE nlohmann_json::nlohmann_json)
```
See [Integration](integration/index.md) for CMake in detail, all supported package managers (Conan, Meson, Bazel,
Conda, and more), and pkg-config.
## Explore the documentation
<div class="grid cards" markdown>
- :octicons-rocket-24:{ .lg .middle } __Features__
@@ -60,5 +112,11 @@ hide:
</div>
![JSON for Modern C++](images/json-light.svg#only-light)
![JSON for Modern C++](images/json-dark.svg#only-dark)
!!! info "Unreleased changes"
This documentation is built from the `develop` branch and may describe changes that are not part of a release
yet. Their version numbers are followed by an <span class="unreleased-version">unreleased</span> badge; see
[Releases](home/releases/index.md) for what shipped in each version.
The library is licensed under the [MIT License](home/license.md). The source code, issue tracker, and discussions
are on [GitHub](https://github.com/nlohmann/json).
+4 -4
View File
@@ -5,13 +5,13 @@ pick one:
```mermaid
flowchart TD
A[Add the library to a C++ project] --> B{{Already using CMake?}}
B -- no --> C{{Using pkg-config or plain Makefiles?}}
A[Add the library to a C++ project] --> B{Already using CMake?}
B -- no --> C{Using pkg-config or plain Makefiles?}
C -- yes --> D[pkg-config]
C -- no --> E[Copy the single header]
B -- yes --> F{{Library installed system-wide?}}
B -- yes --> F{Library installed system-wide?}
F -- yes --> G["find_package()"]
F -- no --> H{{Use a package manager?}}
F -- no --> H{Use a package manager?}
H -- yes --> I[Package manager]
H -- no --> J["add_subdirectory() or FetchContent"]
```
-100
View File
@@ -1,100 +0,0 @@
/*
* Brand look for Mermaid diagrams.
*
* Material for MkDocs loads Mermaid on demand and calls mermaid.initialize() with its own theme CSS. This script
* catches Mermaid when it arrives on window.mermaid and wraps two calls:
* - initialize(): adds the brand's font, spacing and shape rules below to Material's options;
* - render(): marks hexagons ({{...}} in a flowchart) with the class "nj-decision", since in the SVG a hexagon and
* a parallelogram are both a <polygon> that CSS cannot tell apart; and keeps a wide diagram at 80 % of its size
* or more, so its text stays readable and the diagram scrolls sideways instead (see .mermaid in brand.css).
* The colours come from the --md-mermaid-* variables in brand.css, so the diagrams follow the light/dark toggle.
*/
(function () {
"use strict";
var css = [
// text: Inter everywhere, labels on boxes semibold
".label, .nodeLabel, .nodeLabel p, .edgeLabel, .edgeLabel p, .cluster span, text { font-family: var(--md-mermaid-font-family) !important; }",
".nodeLabel, .nodeLabel p { font-weight: 600; }",
".edgeLabel, .edgeLabel p { color: var(--md-default-fg-color--light) !important; font-size: 13px; font-weight: 500; }",
// boxes: 1.5 px outline, 6 px corners; "(...)" nodes stay rounder than "[...]" nodes
".node rect, .node path, .node polygon, .node circle, .node ellipse { stroke-width: 1.5px !important; }",
".node rect:not([rx]) { rx: 6px; ry: 6px; }",
".node rect[rx] { rx: 14px; ry: 14px; }",
// decisions: solid blue hexagons with white text
".node.nj-decision polygon { fill: var(--nj-band) !important; stroke: var(--nj-band) !important; stroke-linejoin: round; }",
".node.nj-decision .nodeLabel, .node.nj-decision .nodeLabel p { color: #fff !important; }",
// lines: 1.5 px, solid unless written as -.-
".edgePath .path, .flowchart-link { stroke-width: 1.5px !important; }",
"marker path { stroke: none !important; }",
// class diagrams; a node with its own fill ("style X fill:#CCCCFF" on the exception pages) is the highlighted
// one and gets soft blue, which also works in dark mode
"g.classGroup rect, .classGroup line, g.node.default > g > path { stroke-width: 1.5px !important; }",
".node path[style*='fill'], .node rect[style*='fill'] { fill: var(--nj-blue-soft) !important; }",
".relation { stroke-width: 1.5px !important; }",
// sequence diagrams
".actor { stroke-width: 1.5px !important; rx: 6px; ry: 6px; }",
".actor-line { stroke-width: 1px; }",
".messageLine0, .messageLine1 { stroke-width: 1.5px !important; }",
".note { rx: 6px; ry: 6px; }"
].join("\n");
var options = {
theme: "base",
fontFamily: "Inter, sans-serif",
themeVariables: { fontFamily: "Inter, sans-serif", fontSize: "15px" },
flowchart: { curve: "basis", padding: 12, nodeSpacing: 36, rankSpacing: 48, wrappingWidth: 240 }
};
var minScale = 0.8;
function postProcess(svg) {
// shrink to fit the column, but not below minScale
svg = svg.replace(/^(<svg[^>]*style="[^"]*)max-width:\s*([\d.]+)px;/, function (all, head, width) {
return head + "max-width: " + width + "px; min-width: " + Math.round(width * minScale) + "px;";
});
// add class "nj-decision" to every flowchart node drawn as a hexagon (a polygon with six corners)
return svg.replace(/<g class="node ([^"]*)"([^>]*)><polygon points="([^"]*)"/g, function (all, cls, rest, points) {
var corners = points.trim().split(/\s+/).length;
return corners === 6 ? '<g class="node nj-decision ' + cls + '"' + rest + '><polygon points="' + points + '"' : all;
});
}
function brand(mermaid) {
if (!mermaid || typeof mermaid.initialize !== "function" || mermaid.njBrand) {
return mermaid;
}
var initialize = mermaid.initialize.bind(mermaid);
var render = mermaid.render.bind(mermaid);
mermaid.initialize = function (config) {
config = Object.assign({}, config || {});
config.theme = options.theme;
config.fontFamily = options.fontFamily;
config.themeVariables = Object.assign({}, config.themeVariables, options.themeVariables);
config.flowchart = Object.assign({}, config.flowchart, options.flowchart);
config.themeCSS = (config.themeCSS || "") + "\n" + css;
return initialize(config);
};
mermaid.render = function () {
return render.apply(null, arguments).then(function (result) {
result.svg = postProcess(result.svg);
return result;
});
};
mermaid.njBrand = true;
return mermaid;
}
// Mermaid ends with globalThis["mermaid"] = ..., which runs the setter below
var current = brand(window.mermaid);
Object.defineProperty(window, "mermaid", {
configurable: true,
get: function () { return current; },
set: function (value) { current = brand(value); }
});
})();
+7 -17
View File
@@ -1,6 +1,5 @@
# Project information
site_name: JSON for Modern C++
site_description: JSON for Modern C++ is a header-only C++11 library that makes JSON a first-class data type.
site_author: Niels Lohmann
site_url: https://json.nlohmann.me/
@@ -15,38 +14,33 @@ copyright: Copyright &copy; 2013-2026 Niels Lohmann
# Configuration
theme:
name: material
custom_dir: overrides
language: en
palette:
- media: '(prefers-color-scheme: light)'
scheme: default
primary: custom
accent: custom
primary: indigo
accent: indigo
toggle:
icon: material/brightness-7
name: Switch to dark mode
- media: '(prefers-color-scheme: dark)'
scheme: slate
primary: custom
accent: custom
primary: indigo
accent: indigo
toggle:
icon: material/brightness-4
name: Switch to light mode
icon:
repo: fontawesome/brands/github
font: false
logo: images/wordmark-header.svg
font:
text: Roboto
code: JetBrains Mono
features:
- navigation.instant
- navigation.tracking
- navigation.tabs
- navigation.indexes
- navigation.top
- navigation.footer
- toc.follow
- search.suggest
- search.highlight
- content.code.copy
- content.tabs.link
- content.action.edit
- content.action.view
@@ -625,7 +619,3 @@ plugins:
extra_css:
- css/custom.css
- css/brand.css
extra_javascript:
- js/mermaid-brand.js
-15
View File
@@ -1,15 +0,0 @@
{#- 404 page: say what happened and offer the three useful ways out -#}
{% extends "main.html" %}
{% block content %}
<div class="nj-404">
<p class="nj-404__code">// error 404</p>
<h1>This page does not exist</h1>
<p>The page may have moved or been renamed. Try the search in the header, or start from one of these pages.</p>
<p>
<a href="{{ '' | url }}" class="md-button md-button--primary">Home</a>
<a href="{{ 'features/' | url }}" class="md-button">Features</a>
<a href="{{ 'api/basic_json/' | url }}" class="md-button">API documentation</a>
</p>
</div>
{% endblock %}
-33
View File
@@ -1,33 +0,0 @@
{#- Home page: the brand's blue hero band below the tabs (selected with "template: home.html" in index.md) -#}
{% extends "main.html" %}
{% block tabs %}
{{ super() }}
<section class="nj-hero">
<div class="nj-hero__inner md-grid">
<div class="nj-hero__text">
<h1 class="nj-hero__title">JSON for <span>Modern C++</span></h1>
<p class="nj-hero__lead">What if JSON was part of modern C++?</p>
<p class="nj-hero__actions">
<a href="{{ 'integration/' | url }}" class="md-button md-button--primary nj-hero__button">Get started</a>
<a href="{{ 'api/basic_json/' | url }}" class="md-button nj-hero__button">API documentation</a>
</p>
</div>
<div class="nj-hero__code" aria-hidden="true">
<pre><code><span class="nj-k">#include</span> <span class="nj-s">&lt;iostream&gt;</span>
<span class="nj-k">#include</span> <span class="nj-s">&lt;nlohmann/json.hpp&gt;</span>
<span class="nj-k">using</span> json = nlohmann::json;
<span class="nj-k">int</span> main()
{
json j = json::parse(<span class="nj-s">R"({"happy": true})"</span>);
j[<span class="nj-s">"pi"</span>] = <span class="nj-n">3.141</span>;
j[<span class="nj-s">"list"</span>] = {<span class="nj-n">1</span>, <span class="nj-n">0</span>, <span class="nj-n">2</span>};
std::cout &lt;&lt; j.dump(<span class="nj-n">4</span>) &lt;&lt; <span class="nj-s">"\n"</span>;
}</code></pre>
</div>
</div>
</section>
{% endblock %}
@@ -1,103 +0,0 @@
"""Draws the parser-callback and iterator illustrations as an inline SVG into docs/images/.
The SVG is not an image: the pages include its text with `--8<-- "images/callback_events.svg, range-begin-end.svg and range-rbegin-rend.svg"`, so it picks up the colours and fonts of the
page (see the `railroad` and `nj-fig` rules in docs/css/brand.css). Run `python3 scripts/figures/iterator_callback_figures.py` from docs/mkdocs after
changing the drawing, and commit the generated file. In SVG text write the word JSON as `J&#8203;SON`, otherwise the
abbreviation extension wraps it in an <abbr> and the text disappears.
"""
import html
from pathlib import Path
OUT = Path(__file__).resolve().parents[2] / "docs" / "images"
CW = 9.6 # JetBrains Mono advance at 16px
def write(name, W, H, cls, tid, title, body, defs=""):
s = ['<svg class="nj-fig %s" viewBox="0 0 %d %d" role="img" aria-labelledby="%s" xmlns="http://www.w3.org/2000/svg">' % (cls, W, H, tid),
'<title id="%s">%s</title>' % (tid, html.escape(title).replace('J&amp;#8203;SON', 'J&#8203;SON'))]
if defs: s.append(defs)
s += body
s.append('</svg>')
open(OUT / name, "w").write("\n".join(s) + "\n")
# ---------- callback events ----------
def callback():
body, y0, lh = [], 74, 28
body.append('<text class="fg-h" x="16" y="28">J&#8203;SON to parse</text>')
body.append('<text class="fg-h" x="320" y="28">callback events</text>')
lines = [
(0, [("{", None), (None, "os")]),
(2, [('"name"', None), (None, "k"), (': "Berlin"', None), (None, "v"), (",", None)]),
(2, [('"location"', None), (None, "k"), (": [", None), (None, "as")]),
(4, [("52.519444", None), (None, "v"), (",", None)]),
(4, [("13.406667", None), (None, "v")]),
(2, [("]", None), (None, "ae"), (None, "v")]),
(0, [("}", None), (None, "oe"), (None, "v")]),
]
for i, (indent, parts) in enumerate(lines):
y = y0 + i * lh
x = 16 + indent * CW
for text, ev in parts:
if text is not None:
body.append('<text class="fg-code" x="%g" y="%d" xml:space="preserve">%s</text>' % (x, y, html.escape(text)))
x += CW * len(text)
else:
body.append('<circle class="cb-%s" cx="%g" cy="%d" r="5.5"/>' % (ev, x + 10, y - 5))
x += 20 if text is None else 0
legend = [("os", "object_start"), ("k", "key"), ("oe", "object_end"), ("as", "array_start"), ("ae", "array_end"), ("v", "value")]
for i, (ev, label) in enumerate(legend):
y = 74 + i * 30
body.append('<circle class="cb-%s" cx="327" cy="%d" r="6"/>' % (ev, y - 5))
body.append('<text class="fg-code fg-leg" x="344" y="%d">%s</text>' % (y, label))
write("callback_events.svg", 480, 262, "nj-fig--cb", "fig-cb-title",
"Example of the parse events a callback sees for the J&#8203;SON text {\"name\": \"Berlin\", \"location\": [52.519444, 13.406667]}: object_start after the opening brace, key after each key, array_start after the opening bracket, value after each value, array_end after the closing bracket, and object_end after the closing brace. Every array and object also produces a value event when it is complete.", body)
# ---------- ranges ----------
BW, GAP, X0 = 30, 4, 30
def cell_x(i): return X0 + i * (BW + GAP)
def cell(i, y, kind):
c = "fg-cell" + (" fg-cell--end" if kind == "end" else "")
return '<rect class="%s" x="%d" y="%d" width="%d" height="30" rx="5"/>' % (c, cell_x(i), y, BW)
def pill(cx, y, label, W, label_w):
x = max(4, min(W - 4 - label_w, cx - label_w / 2))
return ['<rect class="fg-pill" x="%g" y="%d" width="%d" height="24" rx="6"/>' % (x, y, label_w),
'<text class="fg-pilltext" x="%g" y="%d">%s</text>' % (x + label_w / 2, y + 16.5, label)]
def arrow(x, ya, yb, mid, dashed=False):
return '<line class="fg-arrow%s" x1="%g" y1="%g" x2="%g" y2="%g" marker-end="url(#%s)"/>' % (" fg-arrow--dash" if dashed else "", x, ya, x, yb, mid)
def ranges1():
W, H, mid = 600, 150, "fig-be-arrow"
defs = '<defs><marker id="%s" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path class="fg-head" d="M0 1 L10 5 L0 9 z"/></marker></defs>' % mid
body = []
for i in range(15): body.append(cell(i, 62, "elem"))
body.append(cell(15, 62, "end"))
body += pill(cell_x(0) + BW / 2, 8, "begin", W, 60); body.append(arrow(cell_x(0) + BW / 2, 32, 60, mid))
body += pill(cell_x(15) + BW / 2, 8, "end", W, 48); body.append(arrow(cell_x(15) + BW / 2, 32, 60, mid))
body.append('<text class="fg-note fg-note--r" x="%d" y="132">Past-the-last element</text>' % (cell_x(15) + BW / 2 - 18))
body.append(arrow(cell_x(15) + BW / 2, 124, 96, mid))
write("range-begin-end.svg", W, H, "nj-fig--range", "fig-be-title",
"A row of fifteen elements followed by a dashed past-the-last position. begin() points to the first element and end() points to the past-the-last position.", body, defs)
def ranges2():
W, H, mid = 600, 236, "fig-rbe-arrow"
defs = '<defs><marker id="%s" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path class="fg-head" d="M0 1 L10 5 L0 9 z"/></marker></defs>' % mid
body = []
ytop, ybot = 62, 144
body.append(cell(0, ytop, "end"))
for i in range(1, 16): body.append(cell(i, ytop, "elem"))
for i in range(15): body.append(cell(i, ybot, "elem"))
body.append(cell(15, ybot, "end"))
body += pill(cell_x(0) + BW / 2, 8, "rend", W, 52); body.append(arrow(cell_x(0) + BW / 2, 32, 60, mid))
body += pill(cell_x(15) + BW / 2, 8, "rbegin", W, 68); body.append(arrow(cell_x(15) + BW / 2, 32, 60, mid))
body.append('<text class="fg-note fg-note--l" x="%d" y="24">Reverse past-the-last element</text>' % (cell_x(0) + BW / 2 + 36))
for col in (0, 15):
x = cell_x(col) + BW / 2
body.append('<line class="fg-arrow fg-arrow--dash" x1="%g" y1="94" x2="%g" y2="%d"/>' % (x, x, ybot - 2))
body.append('<text class="fg-note" x="300" y="123">A reverse iterator stores an iterator to the next element</text>')
body.append('<text class="fg-note" x="300" y="141" dy="0" style="display:none"></text>')
body += pill(cell_x(0) + BW / 2, 204, "begin", W, 60); body.append(arrow(cell_x(0) + BW / 2, 202, 178, mid))
body += pill(cell_x(15) + BW / 2, 204, "end", W, 48); body.append(arrow(cell_x(15) + BW / 2, 202, 178, mid))
body.append('<text class="fg-note fg-note--r" x="%d" y="221">Past-the-last element</text>' % (cell_x(15) + BW / 2 - 40))
write("range-rbegin-rend.svg", W, H, "nj-fig--range", "fig-rbe-title",
"Two rows of sixteen cells. The upper row shows the reversed sequence: rend() points to a dashed position before the first element and rbegin() to the last element. The lower row shows the forward sequence: begin() points to the first element and end() to a dashed position after the last element. A reverse iterator stores an iterator to the next element, so rend() corresponds to begin() and rbegin() to end().", body, defs)
callback(); ranges1(); ranges2()
@@ -1,76 +0,0 @@
"""Draws the JSON number railroad diagram as an inline SVG into docs/images/.
The SVG is not an image: the pages include its text with `--8<-- "images/json_syntax_number.svg"`, so it picks up the colours and fonts of the
page (see the `railroad` and `nj-fig` rules in docs/css/brand.css). Run `python3 scripts/figures/railroad_number.py` from docs/mkdocs after
changing the drawing, and commit the generated file. In SVG text write the word JSON as `J&#8203;SON`, otherwise the
abbreviation extension wraps it in an <abbr> and the text disappears.
"""
import math
from pathlib import Path
OUT = Path(__file__).resolve().parents[2] / "docs" / "images"
Y0, Y1, Y2, Y3 = 44, 92, 136, 180
R = 10
paths, shapes, texts = [], [], []
def rounded(pts, r=R):
d = "M%g %g" % pts[0]
for i in range(1, len(pts) - 1):
(x0, y0), (x1, y1), (x2, y2) = pts[i-1], pts[i], pts[i+1]
l1 = math.hypot(x1-x0, y1-y0); l2 = math.hypot(x2-x1, y2-y1)
rr = min(r, l1/2, l2/2)
ax, ay = x1 - (x1-x0)/l1*rr, y1 - (y1-y0)/l1*rr
bx, by = x1 + (x2-x1)/l2*rr, y1 + (y2-y1)/l2*rr
d += " L%g %g Q%g %g %g %g" % (ax, ay, x1, y1, bx, by)
d += " L%g %g" % pts[-1]
return d
def path(*pts): paths.append(rounded(list(pts)))
def circle(cx, cy, ch):
shapes.append('<circle class="rr-bg" cx="%g" cy="%g" r="13"/>' % (cx, cy))
shapes.append('<circle class="rr-t" cx="%g" cy="%g" r="13"/>' % (cx, cy))
texts.append('<text class="rr-c" x="%g" y="%g">%s</text>' % (cx, cy+5, ch))
def box(x0, x1, cy, label):
shapes.append('<rect class="rr-bg" x="%g" y="%g" width="%g" height="28" rx="6"/>' % (x0, cy-14, x1-x0))
shapes.append('<rect class="rr-n" x="%g" y="%g" width="%g" height="28" rx="6"/>' % (x0, cy-14, x1-x0))
texts.append('<text class="rr-b" x="%g" y="%g">%s</text>' % ((x0+x1)/2, cy+5, label))
def loop(xr, xl, cy): # return path below a box row
path((xr, cy), (xr, cy+34), (xl, cy+34), (xl, cy))
# start / main
path((8, Y0), (850, Y0))
# A: optional minus
path((24, Y0), (38, Y0), (38, Y1), (59, Y1)); circle(72, Y1, "&#8722;"); path((85, Y1), (106, Y1), (106, Y0), (120, Y0))
# B: 0 | 1-9 digit*
circle(257, Y0, "0")
path((120, Y0), (134, Y0), (134, Y1), (160, Y1)); box(160, 242, Y1, "digit 1&#8211;9")
path((242, Y1), (380, Y1), (380, Y0), (396, Y0))
path((242, Y1), (258, Y1), (258, Y2), (278, Y2)); box(278, 332, Y2, "digit")
path((332, Y2), (348, Y2), (348, Y1)); loop(340, 270, Y2)
# C: fraction
path((396, Y0), (410, Y0), (410, Y1), (425, Y1)); circle(438, Y1, ".")
path((451, Y1), (462, Y1)); box(462, 516, Y1, "digit"); loop(524, 456, Y1)
path((516, Y1), (530, Y1), (530, Y0), (546, Y0))
# D: exponent
path((546, Y0), (560, Y0), (560, Y1), (577, Y1)); circle(590, Y1, "e")
path((546, Y0), (560, Y0), (560, Y2), (577, Y2)); circle(590, Y2, "E")
path((603, Y1), (728, Y1)); path((603, Y2), (620, Y2), (620, Y1))
path((640, Y1), (654, Y1), (654, Y2), (670, Y2)); circle(683, Y2, "&#8722;")
path((640, Y1), (654, Y1), (654, Y3), (670, Y3)); circle(683, Y3, "+")
path((696, Y2), (712, Y2), (712, Y1)); path((696, Y3), (712, Y3), (712, Y1))
path((728, Y1), (746, Y1)); box(746, 800, Y1, "digit"); loop(808, 738, Y1)
path((800, Y1), (816, Y1), (816, Y0), (832, Y0))
svg = ['<svg class="railroad" viewBox="0 0 860 214" role="img" aria-labelledby="rr-title" xmlns="http://www.w3.org/2000/svg">',
'<title id="rr-title">Railroad diagram of the J&#8203;SON number syntax: optional minus, then 0 or a digit from 1 to 9 followed by digits, then an optional fraction (a dot and digits) and an optional exponent (e or E, an optional sign, and digits).</title>',
'<g class="rr-track" fill="none" stroke="currentColor" stroke-opacity=".6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">']
for d in paths: svg.append('<path d="%s"/>' % d)
svg.append('</g>')
svg.append('<polygon class="rr-arrow" points="2,%d 12,%d 2,%d"/>' % (Y0-6, Y0, Y0+6))
svg.append('<polygon class="rr-arrow" points="846,%d 858,%d 846,%d"/>' % (Y0-6, Y0, Y0+6))
svg += shapes + texts
svg.append('<text class="rr-l rr-name" x="24" y="%d">number</text>' % (Y0-12))
svg.append('<text class="rr-l" x="420" y="%d">fraction</text>' % (Y0-12))
svg.append('<text class="rr-l" x="580" y="%d">exponent</text>' % (Y0-12))
svg.append('</svg>')
open(OUT / "json_syntax_number.svg", "w").write("\n".join(svg) + "\n")
-1
View File
@@ -1 +0,0 @@
fonts/
-73
View File
@@ -1,73 +0,0 @@
# Rebuild the animated feature slideshow of the home page (docs/images/json-light.svg and json-dark.svg) when
# slides.md, build.py or the fonts change. Needs uv (build.py declares its dependencies inline, PEP 723), curl,
# unzip and shasum. Run it from docs/mkdocs/scripts/slideshow.
#
# make download missing fonts and build both SVGs
# make fonts download only the fonts
# make SECONDS=12 change the time per slide
# make clean remove the generated SVGs
# make distclean also remove the downloaded fonts
SECONDS ?= 15
IMAGES := ../../docs/images
LIGHT := $(IMAGES)/json-light.svg
DARK := $(IMAGES)/json-dark.svg
RUN := uv run --script build.py
FONTS := fonts/Inter.ttf fonts/JetBrainsMono.ttf
DEPS := build.py slides.md $(FONTS)
# Pinned sources; the checksums make a changed upstream file fail instead of silently changing the output.
# Inter: Google Fonts repository at a fixed commit (variable font with opsz and wght axes).
INTER_URL := https://raw.githubusercontent.com/google/fonts/0b58fb370093f9a9f4ff785d94405710b79de67c/ofl/inter/Inter%5Bopsz%2Cwght%5D.ttf
# JetBrains Mono: release 2.304 (the variable font in fonts/variable/ of the zip).
JBM_URL := https://github.com/JetBrains/JetBrainsMono/releases/download/v2.304/JetBrainsMono-2.304.zip
JBM_ZIP := fonts/JetBrainsMono-2.304.zip
SHA256_Inter.ttf := 29160a80ff49ddcab2c97711247e08b1fab27a484a329ce8b813d820dc559031
SHA256_JetBrainsMono.ttf := 662a196d58f1183bf2d77428b6d5283fe3f45161ab021bea4036bc98e5cac016
SHA256_JetBrainsMono-2.304.zip := 6f6376c6ed2960ea8a963cd7387ec9d76e3f629125bc33d1fdcd7eb7012f7bbf
# Check $@.tmp against its pinned checksum, then move it into place.
define verify
echo '$(SHA256_$(notdir $@)) $@.tmp' | shasum -a 256 -c --quiet - || { rm -f $@.tmp; exit 1; }
mv $@.tmp $@
endef
.PHONY: all fonts clean distclean
.INTERMEDIATE: $(JBM_ZIP)
all: $(LIGHT) $(DARK)
fonts: $(FONTS)
# fixed colours for the docs: an <img> cannot see Material's theme toggle, so the home page includes both
# and shows the one that matches
$(LIGHT): $(DEPS)
$(RUN) $@.tmp $(SECONDS) light
mv $@.tmp $@
$(DARK): $(DEPS)
$(RUN) $@.tmp $(SECONDS) dark
mv $@.tmp $@
fonts/Inter.ttf:
mkdir -p fonts
curl -fsSL -m 300 -o $@.tmp '$(INTER_URL)'
$(verify)
$(JBM_ZIP):
mkdir -p fonts
curl -fsSL -m 300 -o $@.tmp '$(JBM_URL)'
$(verify)
fonts/JetBrainsMono.ttf: | $(JBM_ZIP)
unzip -p $(JBM_ZIP) 'fonts/variable/JetBrainsMono[[]wght].ttf' > $@.tmp
$(verify)
clean:
rm -f $(LIGHT) $(DARK) $(IMAGES)/*.svg.tmp
distclean: clean
rm -f $(FONTS) fonts/*.tmp $(JBM_ZIP)
-27
View File
@@ -1,27 +0,0 @@
# Feature slideshow of the home page
`make` builds `docs/images/json-light.svg` and `json-dark.svg`, the animated feature tour on the home page. They replace
`json.gif` there. All text is outlined, so the SVGs need no fonts. Material's theme toggle cannot be seen from an
`<img>`, so the home page includes both files (`#only-light`, `#only-dark`) and Material shows the one that matches.
The slides are in [`slides.md`](slides.md): one `# Title` per slide (`{badge: TEXT}` at the end adds a red chip), `- `
bullets, and one fenced code block. `build.py` adds the title and closing slides around them, so the slide count follows
the file. Every snippet should compile against the current `develop`. Use a non-breaking space to keep words together
("16 bytes").
```bash
make # download missing fonts, build both SVGs
make SECONDS=12
make clean # remove the SVGs; make distclean also removes the fonts
```
Run it from this directory. It needs [uv](https://docs.astral.sh/uv/), plus curl, unzip and shasum for the fonts:
`build.py` declares its dependencies inline (PEP 723, pinned, so the output stays byte-identical) and `uv run --script`
provides them. `fonts/` is git-ignored: Inter (variable, Google Fonts repository) and JetBrains Mono 2.304 (variable)
are downloaded from pinned URLs and checked against their SHA-256; a changed upstream file stops the build.
`./build.py OUT.svg [seconds] [auto|light|dark]` works directly too. A mistake in `slides.md` stops the build with
`slides.md:LINE` and leaves the old SVGs in place.
Layout: title 48 px, bullets 24 px, code 19 px; the build warns when a slide's code would drop below 18 px. Syntax colours
are the `code-*` tokens of the brand (also used by `css/brand.css`). A CSS animation dissolves each slide over the
previous one and fills the progress bar; with reduced motion only the first slide shows.
-461
View File
@@ -1,461 +0,0 @@
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.9"
# dependencies = ["fonttools==4.67.0", "uharfbuzz==0.57.0"]
# ///
"""Animated SVG slideshow prototype for nlohmann/json, all text outlined (harfbuzz shaping, glyphs as <path> + <use>).
Usage: uv run --script build.py OUT.svg [seconds-per-slide] [auto|light|dark]
auto (the default) follows prefers-color-scheme. light and dark fix the colours, for pages with their own theme
toggle (MkDocs Material), which an <img> cannot see.
The pinned versions keep the output byte-identical; a different harfbuzz may shape slightly differently.
"""
import re, sys, os
import uharfbuzz as hb
from fontTools.pens.svgPathPen import SVGPathPen
HERE = os.path.dirname(os.path.abspath(__file__))
FONTS = os.path.join(HERE, "fonts")
W, H = 1200, 675
TOTAL = 0 # number of slides, set once slides.md is read (for the "02 / 20" labels and the progress segments)
# ---------------------------------------------------------------- glyphs
class Face:
def __init__(self, file, key, feats=None):
blob = hb.Blob.from_file_path(os.path.join(FONTS, file))
self.face = hb.Face(blob)
self.upem = self.face.upem
self.key = key
self.feats = feats or {}
def font(self, var):
f = hb.Font(self.face)
f.set_variations(var)
return f
INTER = Face("Inter.ttf", "i", {"cv11": True, "ss03": True, "kern": True})
MONO = Face("JetBrainsMono.ttf", "m", {"calt": False, "liga": False})
GLYPHS = {} # (face, var, gid) -> id
DEFS = []
def gid_ref(face, font, var, gid):
k = (face.key, tuple(sorted(var.items())), gid)
if k not in GLYPHS:
pen = SVGPathPen(None, ntos=lambda v: str(round(v)))
font.draw_glyph_with_pen(gid, pen)
d = pen.getCommands()
gidname = f"g{len(GLYPHS):x}"
GLYPHS[k] = gidname if d else None
if d:
DEFS.append(f'<path id="{gidname}" d="{d}"/>')
return GLYPHS[k]
def shape(face, text, wght, size, track=0.0):
if not text:
return [], 0, face.upem
var = {"wght": wght}
if face is INTER:
var["opsz"] = max(14, min(32, size))
font = face.font(var)
buf = hb.Buffer()
buf.add_str(text)
buf.guess_segment_properties()
hb.shape(font, buf, face.feats)
out, x = [], 0
tr = track * face.upem
for info, pos in zip(buf.glyph_infos, buf.glyph_positions):
out.append((gid_ref(face, font, var, info.codepoint), x + pos.x_offset, pos.y_offset))
x += pos.x_advance + tr
return out, x - tr, face.upem
def width(text, face=INTER, wght=400, size=16, track=0.0):
_, w, upem = shape(face, text, wght, size, track)
return w * size / upem
def run(text, x, y, size, wght=400, cls="tx", face=INTER, track=0.0):
"""One line of text, baseline at y. Returns (svg, width_px)."""
glyphs, w, upem = shape(face, text, wght, size, track)
s = size / upem
uses = "".join(f'<use href="#{g}" x="{gx}"' + (f' y="{-gy}"' if gy else "") + "/>"
for g, gx, gy in glyphs if g)
return (f'<g class="{cls}" transform="translate({x:g} {y:g}) scale({s:.6g} {-s:.6g})">{uses}</g>', w * s)
def runs(parts, x, y, size, face=INTER, track=0.0):
"""Several (text, wght, cls) pieces on one line."""
out = []
for text, wght, cls in parts:
svg, w = run(text, x, y, size, wght, cls, face, track)
out.append(svg)
x += w + track * size
return "".join(out), x
def wrap(text, maxw, size, wght=400):
lines = _wrap(text, maxw, size, wght)
# no one-word widow: pull the last word of the previous line down if it still fits
if len(lines) > 1 and " " not in lines[-1] and " " in lines[-2]:
head, last = lines[-2].rsplit(" ", 1)
if width(last + " " + lines[-1], size=size, wght=wght) <= maxw:
lines[-2:] = [head, last + " " + lines[-1]]
return lines
def _wrap(text, maxw, size, wght=400):
lines, cur = [], ""
for word in text.split(" "):
t = (cur + " " + word).strip()
if cur and width(t, size=size, wght=wght) > maxw:
lines.append(cur)
cur = word
else:
cur = t
return lines + [cur]
# ---------------------------------------------------------------- pieces
def wordmark(x, y, size, on="page"):
"""The wordmark with hand kerning around the slash (see the logo in the brand kit). Below 32 px it switches to the
small optical master (nlohmann 250 / slash 450), because Thin falls apart at footer size."""
a, sl = ("wm-a", "wm-s") if on == "page" else ("on-a", "on-s")
small = size < 32
w = (250, 450, 800) if small else (100, 400, 800)
k = (0.004, 0.024) if small else (-0.006, 0.012)
out = []
for i, (t, wg, c) in enumerate([("nlohmann", w[0], a), ("/", w[1], sl), ("json", w[2], a)]):
svg, wd = run(t, x, y, size, wg, c, track=-0.02)
out.append(svg)
x += wd - 0.02 * size + (k[i] * size if i < 2 else 0)
return "".join(out), x
KW = {"using", "auto", "return", "const", "struct", "for", "override"}
TYPES = {"int", "void", "bool", "double", "char", "float"}
CONSTANTS = {"true", "false", "nullptr"}
TOKEN = re.compile(r'(//.*$)|(R"\(.*?\)"|R"\(.*$|"(?:[^"\\]|\\.)*"|\'(?:[^\'\\]|\\.)\')|(<[\w/.]+>(?=\s*$))|(#\w+)'
r'|(\b\d+(?:\.\d+)?\b)|(\b[A-Za-z_]\w*\b)|(\s+)|(.)')
def code_block(lines, x, y, size, lh):
"""Lines of C++; a raw string literal may span lines."""
out, in_raw = [], False
for line in lines:
if line:
svg, in_raw = code_line(line, x, y, size, in_raw)
out.append(svg)
y += lh
else:
y += lh * BLANK
return "".join(out)
BLANK = 0.55 # an empty code line is a paragraph break, not a full line
def code_height(lines, size, lh):
n_full = sum(1 for l in lines if l)
n_blank = len(lines) - n_full
return lh * (n_full - 1) + lh * BLANK * n_blank + size
def code_line(line, x, y, size, in_raw=False):
if line.startswith("$ "):
return runs([("$ ", 400, "tx3"), (line[2:], 400, "tx")], x, y, size, MONO)[0], False
out, cx = [], x
if in_raw:
end = line.find(')"')
head = line if end < 0 else line[:end + 2]
svg, w = run(head, cx, y, size, 400, "str", MONO)
out.append(svg)
if end < 0:
return "".join(out), True
cx += w
line = line[end + 2:]
in_raw = False
for m in TOKEN.finditer(line):
com, st, inc, pp, num, ident, ws, other = m.groups()
t = m.group(0)
if st and st.startswith('R"(') and not st.endswith(')"'):
in_raw = True
face, wght, cls = MONO, 400, "tx"
if com:
cls = "com" # upright: JetBrains Mono Italic's cursive f reads as a florin at README size
elif st or inc:
cls = "str"
elif pp:
cls = "pp"
elif ident in KW:
wght, cls = 600, "kw"
elif ident in TYPES:
cls = "ty"
elif num or ident in CONSTANTS:
cls = "num"
elif ident and line[m.end():m.end() + 1] == "(":
cls = "fn" # a call or definition: name directly followed by "("
svg, w = run(t, cx, y, size, wght, cls, face)
if not ws:
out.append(svg)
cx += w
return "".join(out), in_raw
SEG, SEG_GAP = 20, 5
def progress(idx, n_proto, y):
"""TOTAL segments at the bottom right; earlier slides full, current one fills over the slide's time."""
seg, gap = SEG, SEG_GAP
x0 = W - 64 - TOTAL * seg - (TOTAL - 1) * gap
out = []
for k in range(TOTAL):
x = x0 + k * (seg + gap)
out.append(f'<rect class="seg" x="{x}" y="{y}" width="{seg}" height="3" rx="1.5"/>')
if k < idx:
out.append(f'<rect class="seg-on" x="{x}" y="{y}" width="{seg}" height="3" rx="1.5"/>')
elif k == idx:
out.append(f'<rect class="seg-on fill" x="{x}" y="{y}" width="{seg}" height="3" rx="1.5"/>')
return "".join(out)
def pattern_def():
tile = [("{", 14, 34), ("]", 92, 22), (":", 136, 60), ("[", 52, 82), (",", 118, 112),
("}", 20, 140), (":", 78, 150), ("{", 140, 158)]
glyphs = "".join(run(c, x, y, 26, 600, "pat", MONO)[0] for c, x, y in tile)
return f'<pattern id="pat" width="160" height="160" patternUnits="userSpaceOnUse">{glyphs}</pattern>'
# ---------------------------------------------------------------- slides
CODE_SIZES = []
SLOGAN = "What if JSON was part of modern C++?"
def band_bg():
"""Hero gradient plus the syntax pattern, faded out towards the text side (left)."""
return ('<rect width="1200" height="675" fill="url(#hero)"/>'
'<rect width="1200" height="675" fill="url(#pat)" mask="url(#patfade)"/>')
def footer_band(idx, n, left=None):
out = []
if left:
out.append(run(left, 64, FOOT_Y, 20, 600, "on-tx")[0])
out.append(progress(idx, n, FOOT_Y - 7.5).replace('class="seg"', 'class="seg on-band"').replace('class="seg-on', 'class="seg-on on-band'))
return "".join(out)
FOOT_Y = 640 # one footer baseline on every slide: wordmark or URL left, progress right
def slide_title(idx, n):
out = [band_bg()]
x = 96
label = "VERSION 3.13.0"
lw = width(label, MONO, 500, 15, 0.04)
out.append(f'<rect class="chip" x="{x}" y="186" width="{lw + 28:.1f}" height="34" rx="4"/>')
out.append(run(label, x + 14, 208.5, 15, 500, "on-tx", MONO, 0.04)[0])
out.append(wordmark(x - 6, 336, 112, on="band")[0])
out.append(runs([("// ", 400, "w70"), (SLOGAN, 400, "on-s")], x, 404, 26, MONO)[0])
out.append(footer_band(idx, n, "github.com/nlohmann/json"))
return "".join(out)
def slide_feature(idx, n, title, bullets, code, badge=None):
out = ['<rect class="bg" width="1200" height="675"/>']
x, top, bottom = 64, 136, 596
# title row: 48/800, -3 %
svg, tw = run(title, x - 2, 98, 48, 800, "tx", track=-0.03)
out.append(svg)
if badge:
cx = x + tw + 16
cw = width(badge, MONO, 600, 13, 0.06) + 18
out.append(f'<rect class="red" x="{cx:.1f}" y="68.5" width="{cw:.1f}" height="26" rx="4"/>')
out.append(run(badge, cx + 9, 86.2, 13, 600, "on-tx", MONO, 0.06)[0])
# code panel: fixed left edge, fixed width, fixed top on every slide; only its height follows the code
pad, lf = 28, 1.45
px, pw = 488, 1136 - 488
em = max(width(l, MONO, 400, 1) for l in code)
cs = 19.0
while cs > 14 and (em * cs > pw - 2 * pad or code_height(code, cs, lf * cs) > bottom - top - 2 * pad):
cs -= 0.5
CODE_SIZES.append((title, cs))
if cs < 18:
print(f" warning: {title!r} code at {cs} px ({em * 19:.0f} px wide at 19 px)", file=sys.stderr)
lh = lf * cs
ph = max(2 * pad + code_height(code, cs, lh), 240)
out.append(f'<rect class="panel" x="{px}" y="{top}" width="{pw}" height="{ph:.0f}" rx="6"/>')
first = top + pad + cs * 0.78
out.append(code_block(code, px + pad, first, cs, lh))
# bullets: 24/400 on 34, first baseline level with the first code baseline
colw = px - x - 40
by = first
for b in bullets:
out.append(f'<rect class="blk" x="{x}" y="{by - 10.6:.1f}" width="8" height="8" rx="1.5"/>')
for line in wrap(b, colw - 24, 24):
out.append(run(line, x + 24, by, 24, 400, "tx2")[0])
by += 34
by += 16
# footer: small-master wordmark left, progress right, no rule, no numeral
out.append(wordmark(64, FOOT_Y, 22)[0])
out.append(progress(idx, n, FOOT_Y - 7.5))
return "".join(out)
def slide_closing(idx, n):
out = [band_bg()]
x = 96
out.append(wordmark(x - 4, 146, 80, on="band")[0])
out.append(runs([("// ", 400, "w70"), (SLOGAN, 400, "on-s")], x, 200, 22, MONO)[0])
info = [
("{", None),
(' "copyright": ', "(c) 2013-2026 Niels Lohmann"),
(' "license": ', "MIT"),
(' "docs": ', "json.nlohmann.me"),
(' "github": ', "github.com/nlohmann/json"),
(' "sponsor": ', "github.com/sponsors/nlohmann"),
(' "mail": ', "mail@nlohmann.me"),
("}", None),
]
cs, lh, pad = 21, 33, 28
py = 244
pw = max(width(k + '"' + (v or "") + '",', MONO, 400, cs) for k, v in info) + 2 * pad
out.append(f'<rect class="on-panel" x="{x}" y="{py}" width="{pw:.0f}" height="{2 * pad + lh * (len(info) - 1) + cs:.0f}" rx="6"/>')
y = py + pad + cs * 0.8
for k, (key, val) in enumerate(info):
if val is None:
out.append(run(key, x + pad, y, cs, 400, "on-tx", MONO)[0])
else:
comma = "," if k < len(info) - 2 else ""
out.append(runs([(key, 400, "on-tx"), (f'"{val}"', 400, "on-s"), (comma, 400, "on-tx")], x + pad, y, cs, MONO)[0])
y += lh
out.append(footer_band(idx, n))
return "".join(out)
def load_slides(path):
"""Feature slides from Markdown: '# Title {badge: TEXT}', '- bullet' lines, one fenced code block."""
text = re.sub(r"<!--.*?-->", "", open(path).read(), flags=re.S)
slides, cur, in_code = [], None, False
for no, line in enumerate(text.split("\n"), 1):
if in_code:
if line.startswith("```"):
in_code = False
else:
cur["code"].append(line)
elif line.startswith("```"):
in_code = True
elif line.startswith("# "):
title, badge = line[2:].strip(), None
m = re.search(r"\s*\{badge:\s*(.*?)\}$", title)
if m:
title, badge = title[:m.start()], m.group(1)
cur = {"title": title, "bullets": [], "code": [], "badge": badge}
slides.append(cur)
elif line.startswith("- ") and cur:
cur["bullets"].append(line[2:].strip())
elif line[:1] in " \t" and line.strip() and cur and cur["bullets"]:
cur["bullets"][-1] += " " + line.strip()
elif line.strip():
raise SystemExit(f"{path}:{no}: unexpected line: {line!r}")
for sl in slides:
while sl["code"] and not sl["code"][-1].strip():
sl["code"].pop()
if not sl["code"]:
raise SystemExit(f"{path}: slide {sl['title']!r} has no code block")
return slides
FEATURES = load_slides(os.path.join(HERE, "slides.md"))
def _feature(f):
return lambda i, n: slide_feature(i, n, f["title"], f["bullets"], f["code"], f["badge"])
SLIDES = [slide_title] + [_feature(f) for f in FEATURES] + [slide_closing]
TOTAL = len(SLIDES)
# ---------------------------------------------------------------- assemble
def build(sec):
n = len(SLIDES)
T = n * sec
fade = 0.6
a, b, c = fade / T * 100, sec / T * 100, (sec + fade) / T * 100
def cls(i):
return "s s0" if i == 0 else "s sl" if i == n - 1 else "s"
bodies = [f'<g class="{cls(i)}" style="animation-delay:{i * sec:g}s">{fn(i, n)}</g>' for i, fn in enumerate(SLIDES)]
css = f"""
.bg{{fill:#fff}} .tx{{fill:#0f1419}} .tx2{{fill:#3d444f}} .tx3{{fill:#8a919c}} .com{{fill:#646d7a}}
.bl{{fill:#1747a6}} .blk{{fill:#1747a6}} .kw{{fill:#1747a6}} .ty{{fill:#0d6e72}} .fn{{fill:#6b3fb8}} .str{{fill:#23793a}} .num{{fill:#b04a07}} .pp{{fill:#a3247a}}
.panel{{fill:#f5f7fa;stroke:#e3e6eb;stroke-width:1}} .rule{{stroke:#e3e6eb;stroke-width:1}}
.wm-a{{fill:#0f1419}} .wm-s{{fill:#1747a6}}
.on-a,.on-tx{{fill:#fff}} .red{{fill:#d93a3f}} .on-panel{{fill:#fff;fill-opacity:.08;stroke:#fff;stroke-opacity:.16}} .on-s{{fill:#bcd3fb}} .w70{{fill:#fff;fill-opacity:.7}} .on-tx2{{fill:#dbe6fb}} .chip{{fill:#fff;fill-opacity:.14}}
.pat{{fill:#fff;fill-opacity:.09}}
.h1{{stop-color:#1d55c4}} .h2{{stop-color:#123a8a}}
.seg{{fill:#e3e6eb}} .seg-on{{fill:#1747a6}}
.seg.on-band{{fill:#fff;fill-opacity:.25}} .seg-on.on-band{{fill:#fff;fill-opacity:1}}
@media (prefers-color-scheme:dark){{
.bg{{fill:#0f1218}} .tx,.wm-a{{fill:#f2f4f7}} .tx2{{fill:#c3c8d1}} .tx3{{fill:#7b828e}} .com{{fill:#8b929e}}
.bl,.blk,.wm-s,.seg-on,.kw{{fill:#7aa7f5}} .ty{{fill:#4fd1c5}} .fn{{fill:#c4a3ff}} .str{{fill:#7ed491}} .num{{fill:#f5a65b}} .pp{{fill:#f48fc8}}
.panel{{fill:#171b23;stroke:#272c36}} .rule{{stroke:#272c36}} .seg{{fill:#272c36}}
.red{{fill:#c8383d}} .h1{{stop-color:#163f93}} .h2{{stop-color:#0c2556}}
}}
.s{{opacity:0;animation:show {T:g}s linear infinite backwards}}
.fill{{transform-box:fill-box;transform-origin:left;animation:fill {T:g}s linear infinite backwards;animation-delay:inherit}}
.s0{{animation-name:show0}} .sl{{animation-name:showl}}
@keyframes show{{0%{{opacity:0;animation-timing-function:cubic-bezier(.2,.6,.2,1)}} {a:.4f}%{{opacity:1}} {c:.4f}%{{opacity:1}} {c + .01:.4f}%{{opacity:0}} 100%{{opacity:0}}}}
@keyframes show0{{0%{{opacity:1}} {c:.4f}%{{opacity:1}} {c + .01:.4f}%{{opacity:0}} 100%{{opacity:0}}}}
@keyframes showl{{0%{{opacity:0;animation-timing-function:cubic-bezier(.2,.6,.2,1)}} {a:.4f}%{{opacity:1}} {b:.4f}%{{opacity:1}} {c:.4f}%{{opacity:0}} 100%{{opacity:0}}}}
@keyframes fill{{0%{{transform:scaleX(0)}} {b:.4f}%{{transform:scaleX(1)}} 100%{{transform:scaleX(1)}}}}
@media (prefers-reduced-motion:reduce){{.s,.fill{{animation:none}} .s:first-of-type{{opacity:1}}}}
"""
css = re.sub(r"\n\s*", "", css)
pat = pattern_def()
return (f'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 {W} {H}" width="{W}" height="{H}" role="img">'
f'<title>JSON for Modern C++: feature slideshow (animated, {n} slides, {sec:g} s each)</title>'
f'<style>{css}</style>'
f'<defs>{"".join(DEFS)}{pat}'
f'<linearGradient id="hero" gradientTransform="rotate(70 .5 .5)"><stop class="h1" offset="0"/><stop class="h2" offset="1"/></linearGradient>'
f'<linearGradient id="fadeg" x1="0" x2="1" y1="0" y2="0"><stop offset=".25" stop-color="#fff" stop-opacity="0"/><stop offset=".85" stop-color="#fff" stop-opacity="1"/></linearGradient>'
f'<mask id="patfade" maskUnits="userSpaceOnUse" x="0" y="0" width="{W}" height="{H}"><rect width="{W}" height="{H}" fill="url(#fadeg)"/></mask></defs>'
f'<rect class="bg" width="{W}" height="{H}"/>'
f'{"".join(bodies)}</svg>')
def fix_scheme(svg, scheme):
"""Drop the dark-mode block (light) or apply it unconditionally (dark)."""
if scheme == "auto":
return svg
start = svg.index("@media (prefers-color-scheme:dark){")
i, depth = svg.index("{", start), 0
for j in range(i, len(svg)):
depth += {"{": 1, "}": -1}.get(svg[j], 0)
if depth == 0:
break
inner = svg[i + 1:j] if scheme == "dark" else ""
return svg[:start] + inner + svg[j + 1:]
if __name__ == "__main__":
out = sys.argv[1]
sec = float(sys.argv[2]) if len(sys.argv) > 2 else 15
scheme = sys.argv[3] if len(sys.argv) > 3 else "auto"
if scheme not in ("auto", "light", "dark"):
sys.exit(f"unknown colour scheme {scheme!r}: use auto, light or dark")
svg = fix_scheme(build(sec), scheme)
# defs are appended while slides render, so build the slides first (done inside build via f-string order)
open(out, "w").write(svg)
print(out, len(svg.encode()) // 1024, "KB,", len(GLYPHS), "glyphs; code px:", " ".join(f"{c:g}" for _, c in CODE_SIZES))
-412
View File
@@ -1,412 +0,0 @@
<!--
Feature slides for the animated slideshow. build.py adds the title slide before them and the closing slide after them.
# Title one slide per heading; `{badge: TEXT}` at the end adds a red chip
- bullet bullets; a long bullet may continue on indented lines
```cpp ... ``` the code shown on the right (a line starting with `$ ` is a shell command)
-->
# Trivial integration
- single header file
- no build system needed
- vanilla C++11
- works with GCC 4.8+, Clang 3.4+ and MSVC 2015+
```cpp
#include <nlohmann/json.hpp>
// for convenience
using json = nlohmann::json;
int main()
{
// a JSON pretty-printer in 3 lines
json j;
std::cin >> j;
std::cout << std::setw(4) << j << std::endl;
}
$ g++ pretty.cpp -std=c++11 -o pretty
```
# First-class datatype
- write JSON like any other C++ literal
- initializer lists become arrays and objects
```cpp
json j = {
{"pi", 3.141},
{"happy", true},
{"name", "Niels"},
{"nothing", nullptr},
{"answer", {
{"everything", 42}
}},
{"list", {1, 0, 2}},
{"object", {
{"currency", "USD"},
{"value", 42.99}
}}
};
```
# Array construction
- arrays work the way you expect
- adding to a null value makes it an array
```cpp
// create null value
json j;
// add via member function
j.push_back("Hello");
j.emplace_back("World");
// add via += operator
j += false;
// add via index
j[2] = 42.3;
// gaps are filled with null
j[4] = "World";
```
# Object construction
- objects work the way you expect
- adding to a null value makes it an object
```cpp
// create null value
json j;
// add via member function
j.emplace("key2", "value");
j.push_back({"key", "value"});
// add via += operator
j += {"another key", false};
// add via key
j["pi"] = 3.1415926;
j["answer"]["everything"] = 42;
```
# Container operations
- use JSON like an STL container
- satisfies the ReversibleContainer requirements
```cpp
// container empty?
j.empty();
// current number of elements
j.size();
// maximal number of elements
j.max_size();
// swap elements
j.swap(j2);
std::swap(j, j2);
// clear a container
j.clear();
```
# STL containers
- switch between STL and JSON containers
- conversion works in both directions
```cpp
// some STL containers
std::vector<int> v;
std::unordered_map<std::string, bool> m;
// automatic transformation
json j_array(v);
json j_object(m);
// works both ways
auto l = j_array.get<std::list<int>>();
auto u = j_object
.get<std::map<std::string, bool>>();
```
# Type conversion
- convert your own types to and from JSON
- one macro writes to_json and from_json
- or write both functions for full control
```cpp
// some user-defined type
struct person {
std::string name;
int age;
};
// defines to_json and from_json
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person,
name, age)
// conversion in action
person p {"Ned Flanders", 60};
json j = p;
// {"age": 60, "name": "Ned Flanders"}
auto p2 = j.get<person>();
```
# Type deduction
- types are deduced automatically
- member functions tell you the type
```cpp
// type deduction
json j1 = nullptr;
json j2 = false;
json j3 = 12.23;
json j4 = {1, 2, 3};
json j5 = {{"key", "value"}};
json j6 = "Hello";
// quick checks
j1.is_null();
j2.is_boolean();
j3.is_number();
j4.is_array();
j5.is_object();
j6.is_string();
```
# Serialization
- to a string or a stream
- pretty-printing included
```cpp
// default serialization
auto s1 = j.dump();
// pretty-printed
int indent = 4;
std::string s2 = j.dump(indent);
// to stream
std::cout << j << std::endl;
// pretty-printed
std::cout << std::setw(indent) << j << std::endl;
```
# Deserialization
- parse strings, streams and files
- the _json literal puts plain JSON in your code
- comments and trailing commas on request
```cpp
// from string
json j1 = json::parse("[1,null,false]");
// from file
std::ifstream f("example.json");
json j2 = json::parse(f);
// from string literal
json j3 = "[3.14159]"_json;
// from raw string literal
json j4 = R"(
{"one": 1, "two": 2, "three": [1, 2, 3]}
)"_json;
```
# SAX parsing
- the parser can emit SAX events
- process input without building a JSON value
```cpp
// define a SAX event processor
struct sax_processor : json::json_sax_t
{
bool boolean(bool val) override
{
return true;
}
// implement other functions...
};
// create object
sax_processor my_processor;
// use it to process input
json::sax_parse(input, &my_processor);
```
# Zero-copy views {badge: NEW IN 3.13}
- parse without building a tree: 16 bytes per value
- strings and numbers stay in the source text
- materialize a json value only where you need one
```cpp
#include <nlohmann/json_view.hpp>
// parse into a flat index, not a tree
std::ifstream f("big.json");
auto doc = nlohmann::json_document::parse(f);
// read-only views with familiar access
auto view = doc.root();
double pi = view["pi"].get<double>();
for (auto v : view["list"])
std::cout << v.get<int>() << '\n';
// build a json value only where needed
json part = view["list"].materialize();
```
# JSON Pointer
- address any element with a JSON Pointer
- flatten a value to pointer/value pairs, and back
```cpp
json j = {"good", "bad", "ugly"};
// JSON pointer to the 3rd list element
auto ptr = json::json_pointer("/2");
// use JSON pointer to access element
j[ptr] = {{"it", "cattivo"}};
// user-defined string literal
j["/2/en"_json_pointer] = "ugly";
// ["good","bad",{"en":"ugly","it":"cattivo"}]
json flat = j.flatten();
// {"/0":"good","/1":"bad","/2/en":"ugly",
// "/2/it":"cattivo"}
json j2 = flat.unflatten(); // j == j2
```
# JSON Patches
- use JSON Patch to describe edit operations
- create diffs from two JSON values
- or use a JSON Merge Patch that looks like the document
```cpp
json source = R"({"F": "Paris", "D": "Berlin"})"_json;
// a JSON Patch (RFC 6902)
json p1 = R"([
{"op": "add", "path": "/GB", "value": "London"}
])"_json;
json target = source.patch(p1);
// create a diff from two JSON values
json p2 = json::diff(target, source);
// [{"op": "remove", "path": "/GB"}]
// a JSON Merge Patch (RFC 7396)
target.merge_patch(R"({"F": null})"_json);
// {"D": "Berlin", "GB": "London"}
```
# Binary formats
- compact binary representations
- BJData, BON8, BSON, CBOR, MessagePack and UBJSON
```cpp
// a JSON value
json j = R"({"compact": true, "schema": 0})"_json;
// serialize to CBOR
std::vector<std::uint8_t> v = json::to_cbor(j);
// 0xA2, 0x67, 0x63, 0x6F, 0x6D, 0x70, 0x61,
// 0x63, 0x74, 0xF5, 0x66, 0x73, 0x63, 0x68,
// 0x65, 0x6D, 0x61, 0x00
// roundtrip
json j_from_cbor = json::from_cbor(v);
// the same for the other formats
auto b = json::to_bon8(j);
auto m = json::to_msgpack(j);
```
# Algorithms
- the standard algorithms just work
- sort, find, count, merge and more
```cpp
// sort JSON containers
std::sort(j.begin(), j.end());
// find element
auto pos = std::find(j.begin(), j.end(),
json("needle"));
// count all numbers
auto n = std::count_if(j.begin(), j.end(),
[](const json& v) { return v.is_number(); });
// merge two sorted containers
std::merge(j1.begin(), j1.end(),
j2.begin(), j2.end(),
std::back_inserter(j3));
```
# Iterators
- iterators, const iterators, and reverse iterators
- random access for arrays
- items() gives you keys and values
```cpp
json j = {1, 2, 3, 4, 5};
// range for
for (auto& el : j)
{
// print all values
std::cerr << el << '\n';
}
// classical iterators
for (auto it = j.begin(); it != j.end(); ++it)
{
// double all values
*it = it->get<int>() * 2;
}
```
# Structured bindings
- since C++17, unpack keys and values with structured bindings
```cpp
// JSON object
auto j = R"({"foo": 23, "bar": 42})"_json;
// structured bindings
for (auto& [key, val] : j.items())
{
std::cerr << key << ':' << val << '\n';
}
```
+9 -1
View File
@@ -543,7 +543,15 @@ run_found:
NLOHMANN_VIEW_NOINLINE void arena_grow(std::size_t n)
{
const std::size_t used = arena_used();
m_doc.arena.resize((std::max)(m_doc.arena.size() * 2, used + n + 256));
std::size_t wanted = used + n + 256;
if (m_doc.arena.empty())
{
// the first growth: a decoded string is never longer than its source,
// so a fraction of the input (capped, as the buffer is zero-filled)
// covers the escapes of a typical document without regrowing it
wanted = (std::max)(wanted, (std::min)((static_cast<std::size_t>(m_end - m_begin) / 4) + 16, std::size_t{4096}));
}
m_doc.arena.resize((std::max)(m_doc.arena.size() * 2, wanted));
m_arena_write = m_doc.arena.data() + used;
m_arena_end = m_doc.arena.data() + m_doc.arena.size();
}
@@ -82,14 +82,17 @@ struct document_data
};
std::unique_ptr<edit_state> edits; ///< created by the first edit
/// the most nodes that are allocated together with the header (16 KiB)
static constexpr std::size_t max_inline_nodes = 1024;
/// One allocation for the header and room for `count` nodes, so that a
/// small document needs one allocation instead of two. Large documents get
/// a separate node array instead (so it can be trimmed). When a document
/// outgrows the inline array, that array stays unused: at most 256 nodes
/// (4 KiB), and only for documents whose estimate was too small.
/// outgrows the inline array, that array stays unused: at most 1024 nodes
/// (16 KiB), and only for documents whose estimate was too small.
static document_data* create(std::size_t count)
{
count = count <= 256 ? count : 0;
count = count <= max_inline_nodes ? count : 0;
void* mem = ::operator new (sizeof(document_data) + (count * sizeof(node)));
auto* d = new (mem) document_data(); // NOLINT(cppcoreguidelines-owning-memory): owned by the returned pointer, freed by deleter
// (aligned: sizeof is a multiple of the alignment; through void*, as GCC's -Wcast-align wants)
+15 -4
View File
@@ -397,14 +397,17 @@ struct document_data
};
std::unique_ptr<edit_state> edits; ///< created by the first edit
/// the most nodes that are allocated together with the header (16 KiB)
static constexpr std::size_t max_inline_nodes = 1024;
/// One allocation for the header and room for `count` nodes, so that a
/// small document needs one allocation instead of two. Large documents get
/// a separate node array instead (so it can be trimmed). When a document
/// outgrows the inline array, that array stays unused: at most 256 nodes
/// (4 KiB), and only for documents whose estimate was too small.
/// outgrows the inline array, that array stays unused: at most 1024 nodes
/// (16 KiB), and only for documents whose estimate was too small.
static document_data* create(std::size_t count)
{
count = count <= 256 ? count : 0;
count = count <= max_inline_nodes ? count : 0;
void* mem = ::operator new (sizeof(document_data) + (count * sizeof(node)));
auto* d = new (mem) document_data(); // NOLINT(cppcoreguidelines-owning-memory): owned by the returned pointer, freed by deleter
// (aligned: sizeof is a multiple of the alignment; through void*, as GCC's -Wcast-align wants)
@@ -1734,7 +1737,15 @@ run_found:
NLOHMANN_VIEW_NOINLINE void arena_grow(std::size_t n)
{
const std::size_t used = arena_used();
m_doc.arena.resize((std::max)(m_doc.arena.size() * 2, used + n + 256));
std::size_t wanted = used + n + 256;
if (m_doc.arena.empty())
{
// the first growth: a decoded string is never longer than its source,
// so a fraction of the input (capped, as the buffer is zero-filled)
// covers the escapes of a typical document without regrowing it
wanted = (std::max)(wanted, (std::min)((static_cast<std::size_t>(m_end - m_begin) / 4) + 16, std::size_t{4096}));
}
m_doc.arena.resize((std::max)(m_doc.arena.size() * 2, wanted));
m_arena_write = m_doc.arena.data() + used;
m_arena_end = m_doc.arena.data() + m_doc.arena.size();
}
+55
View File
@@ -666,6 +666,61 @@ TEST_CASE("json_view")
CHECK(small.root().materialize() == json::parse("[1,[2,3],{\"a\":\"b\\n\"}]"));
}
SECTION("block sizes")
{
// documents whose node estimate lies around the size that is allocated
// with the header, and whose decoded strings need the first growth of
// the buffer, in all sizes around the points where the policies change
for (const int count :
{
1, 2, 10, 50, 100, 255, 256, 257, 500, 1000, 1023, 1024, 1025, 2000, 5000
})
{
// strings with escapes (decoded text, minified)
std::string escaped = "[";
for (int i = 0; i < count; ++i)
{
escaped += (i != 0 ? ",\"a\\n" : "\"a\\n") + std::to_string(i) + "\\u00e4\"";
}
escaped += ']';
json_document e = json_document::parse(escaped);
CHECK(e.root().materialize() == json::parse(escaped));
e.shrink_to_fit();
CHECK(e.root().materialize() == json::parse(escaped));
// dense numbers (more nodes per byte than the estimate expects)
std::string dense = "[";
for (int i = 0; i < count; ++i)
{
dense += (i != 0 ? ",1" : "1");
}
dense += ']';
json_document n = json_document::parse(dense);
CHECK(n.node_count() == static_cast<std::size_t>(count) + 1);
CHECK(n.root().materialize() == json::parse(dense));
n.shrink_to_fit();
CHECK(n.root().size() == static_cast<std::size_t>(count));
// the same documents, pretty-printed
const std::string pretty = json::parse(escaped).dump(2);
json_document p = json_document::parse(pretty);
CHECK(p.root().materialize() == json::parse(escaped));
}
// a large document with a single escape does not need a buffer of the
// size of its input
std::string large = R"(["a\n")";
for (int i = 0; i < 100000; ++i)
{
large += ",1";
}
large += ']';
const json_document l = json_document::parse(large);
CHECK(l.root().size() == 100001);
CHECK(l.root()[0].get_string() == "a\n");
CHECK(l.root()[100000].get<int>() == 1);
}
SECTION("source offsets")
{
const std::string text = R"( {"key": "value", "escaped": "a\nb", "n": 42})";