Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44f1bec789 |
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"]
|
||||
```
|
||||
|
||||
@@ -7,7 +7,9 @@ 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>
|
||||
|
||||
## Return value
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@ 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>
|
||||
|
||||
## Return value
|
||||
|
||||
|
||||
@@ -15,13 +15,11 @@ error
|
||||
: report a parse error in case of a tag (the `from_cbor` overloads throw a `parse_error` exception by default)
|
||||
|
||||
ignore
|
||||
: ignore tags (also in front of a text string map key)
|
||||
: ignore tags
|
||||
|
||||
store
|
||||
: store tagged byte strings (for bytes 0xd8..0xdb) as binary values with the tag as subtype; other tagged values are
|
||||
read as if the tag were ignored. If several tags precede a byte string, only the tag directly in front of it is
|
||||
stored, and only when it has the form 0xd8..0xdb (a tag 0..23 written as 0xc0..0xd7 is skipped). Tags in front of a
|
||||
text string map key are skipped.
|
||||
read as if the tag were ignored. If several tags precede a byte string, only the innermost one is stored.
|
||||
|
||||
## Examples
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@ 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>
|
||||
|
||||
## Return value
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@ 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>
|
||||
|
||||
## Return value
|
||||
|
||||
|
||||
@@ -7,7 +7,9 @@ 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>
|
||||
|
||||
## Return value
|
||||
|
||||
|
||||
@@ -7,7 +7,9 @@ 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>
|
||||
|
||||
## Return value
|
||||
|
||||
|
||||
@@ -32,10 +32,8 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
||||
|
||||
- an `std::istream` object
|
||||
- a `FILE` pointer
|
||||
- a C-style array of characters (its terminating NUL is read as trailing data: `parse_error.110` unless `strict` is
|
||||
`false`)
|
||||
- a pointer to a null-terminated string of single byte characters (read up to the first 0x00 byte, which is a valid
|
||||
CBOR byte)
|
||||
- a C-style array of characters
|
||||
- a pointer to a null-terminated string of single byte characters
|
||||
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
|
||||
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
|
||||
|
||||
@@ -91,14 +89,8 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
||||
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if unsupported features from CBOR were
|
||||
used in the given input or if the input is not valid CBOR
|
||||
- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a map key is not a string (keys of
|
||||
other types are not supported, as JSON object keys are always strings), if the head of a string or byte string is
|
||||
malformed (including a chunk of the wrong type or an indefinite-length string nested inside an indefinite-length
|
||||
string), or if a string value or object key (or one chunk of an indefinite-length string) is not valid UTF-8 and
|
||||
`error_handler` is `strict`
|
||||
- Throws [out_of_range.408](../../home/exceptions.md#jsonexceptionout_of_range408) if an array or map announces
|
||||
a length that `std::size_t` cannot hold, or that equals `std::size_t(-1)` (for example `9B FF FF FF FF FF FF FF FF`)
|
||||
- Throws [out_of_range.406](../../home/exceptions.md#jsonexceptionout_of_range406) if a floating-point number does not
|
||||
fit into `number_float_t`
|
||||
other types are not supported, as JSON object keys are always strings), or if a string value or object key is not
|
||||
valid UTF-8 and `error_handler` is `strict`
|
||||
|
||||
## Complexity
|
||||
|
||||
|
||||
@@ -22,7 +22,9 @@ The parser callback distinguishes the following events:
|
||||
|
||||
## Examples
|
||||
|
||||

|
||||
<div class="nj-fig-wrap" markdown>
|
||||
--8<-- "images/callback_events.svg"
|
||||
</div>
|
||||
|
||||
??? example
|
||||
|
||||
|
||||
@@ -23,7 +23,9 @@ 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>
|
||||
|
||||
Discarding a value (i.e., returning `#!cpp false`) has different effects depending on the context in which function was
|
||||
called:
|
||||
|
||||
@@ -7,7 +7,9 @@ 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>
|
||||
|
||||
## Return value
|
||||
|
||||
|
||||
@@ -8,7 +8,9 @@ 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>
|
||||
|
||||
## Return value
|
||||
|
||||
|
||||
@@ -42,8 +42,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
||||
|
||||
## Exception safety
|
||||
|
||||
Strong guarantee: if an exception is thrown, there are no changes in the JSON value. Bytes that were already written to
|
||||
the output adapter are not taken back.
|
||||
Strong guarantee: if an exception is thrown, there are no changes in the JSON value.
|
||||
|
||||
## Exceptions
|
||||
|
||||
|
||||
@@ -0,0 +1,739 @@
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 408 B |
@@ -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"]
|
||||
|
||||
@@ -11,7 +11,7 @@ extremely small code sizes, fairly small message size, and extensibility without
|
||||
|
||||
## Byte layout
|
||||
|
||||
CBOR encodes every data item as a *head* followed by optional content. The first byte of the head holds the *major type* in its upper three bits (unsigned integer, negative integer, byte string, text string, array, map, tag, or simple/float) and an *additional information* value in its lower five bits. Additional information below 24 is the argument itself (for example, a small integer or a short length); the values 24 to 27 announce that the argument follows in 1, 2, 4, or 8 big-endian bytes. Strings, arrays, and maps are prefixed with their length (or count of elements/pairs), and content follows directly. A reader of definite-length items knows how many items to expect without a terminator; indefinite-length items (additional information 31) are instead closed by the "break" byte 0xFF, which `from_cbor` reads but `to_cbor` never writes.
|
||||
CBOR encodes every data item as a *head* followed by optional content. The first byte of the head holds the *major type* in its upper three bits (unsigned integer, negative integer, byte string, text string, array, map, tag, or simple/float) and an *additional information* value in its lower five bits. Additional information below 24 is the argument itself (for example, a small integer or a short length); the values 24 to 27 announce that the argument follows in 1, 2, 4, or 8 big-endian bytes. Strings, arrays, and maps are prefixed with their length (or count of elements/pairs), and content follows directly, so a reader always knows how many items to expect without a terminator.
|
||||
|
||||
Consider the following JSON value (shown formatted for readability):
|
||||
|
||||
@@ -52,16 +52,16 @@ The library uses the following mapping from JSON values types to CBOR types acco
|
||||
| null | `null` | Null | 0xF6 |
|
||||
| boolean | `true` | True | 0xF5 |
|
||||
| boolean | `false` | False | 0xF4 |
|
||||
| number_integer | -9223372036854775808..-4294967297 | Negative integer (8 bytes follow) | 0x3B |
|
||||
| number_integer | -4294967296..-65537 | Negative integer (4 bytes follow) | 0x3A |
|
||||
| number_integer | -65536..-257 | Negative integer (2 bytes follow) | 0x39 |
|
||||
| number_integer | -256..-25 | Negative integer (1 byte follow) | 0x38 |
|
||||
| number_integer | -9223372036854775808..-2147483649 | Negative integer (8 bytes follow) | 0x3B |
|
||||
| number_integer | -2147483648..-32769 | Negative integer (4 bytes follow) | 0x3A |
|
||||
| number_integer | -32768..-129 | Negative integer (2 bytes follow) | 0x39 |
|
||||
| number_integer | -128..-25 | Negative integer (1 byte follow) | 0x38 |
|
||||
| number_integer | -24..-1 | Negative integer | 0x20..0x37 |
|
||||
| number_integer | 0..23 | Integer | 0x00..0x17 |
|
||||
| number_integer | 24..255 | Unsigned integer (1 byte follow) | 0x18 |
|
||||
| number_integer | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 |
|
||||
| number_integer | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A |
|
||||
| number_integer | 4294967296..9223372036854775807 | Unsigned integer (8 bytes follow) | 0x1B |
|
||||
| number_integer | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B |
|
||||
| number_unsigned | 0..23 | Integer | 0x00..0x17 |
|
||||
| number_unsigned | 24..255 | Unsigned integer (1 byte follow) | 0x18 |
|
||||
| number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 |
|
||||
@@ -95,24 +95,7 @@ see "binary" cells in the table above.
|
||||
|
||||
!!! success "Complete mapping"
|
||||
|
||||
The mapping is **complete** in the sense that any JSON value type can be converted to a CBOR value, with one
|
||||
exception: an integer outside the range of CBOR integers (only possible with a number type wider than 64 bits)
|
||||
throws [`out_of_range.407`](../../home/exceptions.md#jsonexceptionout_of_range407).
|
||||
|
||||
!!! info "Not a deterministic encoding"
|
||||
|
||||
`to_cbor` does not produce the deterministic encoding of
|
||||
[RFC 8949, Section 4.2.1](https://www.rfc-editor.org/rfc/rfc8949.html#section-4.2.1), and not always the preferred
|
||||
serialization of Section 4.1:
|
||||
|
||||
- floating-point numbers are written as single precision (0xFA) when that is exact and as double precision (0xFB)
|
||||
otherwise; half precision (0xF9) is used only for `NaN` and `Infinity`/`-Infinity` (`1.0` is `fa 3f 80 00 00`);
|
||||
- a binary subtype 0..23 is written as `d8 xx` instead of the one-byte tag 0xC0..0xD7 (`d8 01 41 01`);
|
||||
- object members are written in the order of `object_t` (for the default `std::map`: sorted by the key string, not
|
||||
by the encoded key bytes).
|
||||
|
||||
Integers and lengths use the shortest form, and indefinite-length items are never written. Decoders must accept all
|
||||
of this; the RFC does not require the preferred serialization.
|
||||
The mapping is **complete** in the sense that any JSON value type can be converted to a CBOR value.
|
||||
|
||||
!!! info "NaN/infinity handling"
|
||||
|
||||
@@ -122,8 +105,9 @@ see "binary" cells in the table above.
|
||||
|
||||
!!! note
|
||||
|
||||
Prior to version 3.8.0, NaN and Infinity were instead serialized as a CBOR double-precision float (type 0xFB,
|
||||
9 bytes total).
|
||||
Prior to version 3.13.0, NaN and Infinity were instead serialized as a CBOR double-precision float (type 0xFB,
|
||||
9 bytes total), because the check used to select a smaller encoding compared magnitudes with NaN, which is
|
||||
always `false` and caused the intended half-precision path to be skipped.
|
||||
|
||||
!!! info "Unused CBOR types"
|
||||
|
||||
@@ -140,6 +124,7 @@ see "binary" cells in the table above.
|
||||
- expected conversions (0xD5..0xD7)
|
||||
- simple values (0xE0..0xF3, 0xF8)
|
||||
- undefined (0xF7)
|
||||
- half-precision floats (0xF9)
|
||||
- break (0xFF)
|
||||
|
||||
!!! info "Tagged items"
|
||||
@@ -241,20 +226,10 @@ string of the same major type, as required by [RFC 8949, Section 3.2.3](https://
|
||||
([RFC 8392](https://www.rfc-editor.org/rfc/rfc8392.html)), cannot be read with this library and need a
|
||||
general-purpose CBOR library instead.
|
||||
|
||||
A tag in front of a text string key is handled like a tag in front of a value: it is skipped with
|
||||
`cbor_tag_handler_t::ignore` or `cbor_tag_handler_t::store` and rejected with the default `error`. A tag does not
|
||||
turn a key of another type into a text string.
|
||||
|
||||
A map with duplicate keys is well-formed, but invalid under
|
||||
[RFC 8949, Section 5.3.1](https://www.rfc-editor.org/rfc/rfc8949.html#section-5.3.1). `from_cbor` accepts it and the
|
||||
last value wins; the [SAX interface](../parsing/sax_interface.md) reports every occurrence.
|
||||
|
||||
!!! warning "Ill-formed UTF-8 in text strings"
|
||||
|
||||
[RFC 8949, Section 3.1](https://www.rfc-editor.org/rfc/rfc8949.html#section-3.1) requires CBOR text strings (major
|
||||
type 3) to be valid UTF-8 (a string with ill-formed UTF-8 is "well-formed but invalid"), and
|
||||
[Section 5.3.1](https://www.rfc-editor.org/rfc/rfc8949.html#section-5.3.1) leaves it up to the decoder whether to
|
||||
verify this, so checking is opt-in: with the
|
||||
type 3) to be valid UTF-8, but leaves it up to the decoder whether to enforce this, so checking is opt-in: with the
|
||||
[`error_handler`](../../api/basic_json/from_cbor.md) parameter left at `keep` (the default), `from_cbor()` accepts a
|
||||
text string (object keys included) whose bytes are not valid UTF-8 and hands them back unchanged. Passing
|
||||
`error_handler_t::strict` makes `from_cbor()` check and throw
|
||||
@@ -267,14 +242,9 @@ string of the same major type, as required by [RFC 8949, Section 3.2.3](https://
|
||||
[`JSON_STRICT_BINARY_UTF8`](../../api/macros/json_strict_binary_utf8.md) is enabled), it throws the same exception
|
||||
instead. Byte strings (major type 2) are unaffected, since they are not required to hold text.
|
||||
|
||||
The chunks of an indefinite-length text string are checked one by one, as required by
|
||||
[RFC 8949, Section 3.2.3](https://www.rfc-editor.org/rfc/rfc8949.html#section-3.2.3): a code point cannot be spread
|
||||
over two chunks. With `error_handler_t::strict` such a string throws `parse_error.113`; `replace` and `ignore`
|
||||
sanitize each chunk on its own.
|
||||
|
||||
!!! warning "Tagged items"
|
||||
|
||||
Tagged items (0xC0..0xDB) will throw a parse error by default. They can be ignored by passing `cbor_tag_handler_t::ignore` to function `from_cbor`, in which case the tag is skipped and the enclosed data item is parsed on its own. Passing `cbor_tag_handler_t::store` to function `from_cbor` stores tagged byte strings (for bytes 0xd8..0xdb) as binary values with the tag as subtype; other tagged values are read as if the tag were ignored. If several tags precede a byte string, only the tag directly in front of it is stored, and only when it has the form 0xD8..0xDB (a tag 0..23 written as 0xC0..0xD7 is skipped); for example, `D8 05 C1 41 AA` yields subtype `null`, whereas `C1 D8 05 41 AA` yields 5. Tags in front of a text string map key are skipped as well. Note that no tag is ever interpreted: for instance, a text string tagged with tag 0 (date/time) stays a string.
|
||||
Tagged items (0xC0..0xDB) will throw a parse error by default. They can be ignored by passing `cbor_tag_handler_t::ignore` to function `from_cbor`, in which case the tag is skipped and the enclosed data item is parsed on its own. Passing `cbor_tag_handler_t::store` to function `from_cbor` stores tagged byte strings (for bytes 0xd8..0xdb) as binary values with the tag as subtype; other tagged values are read as if the tag were ignored. If several tags precede a byte string, only the innermost one is stored. Note that no tag is ever interpreted: for instance, a text string tagged with tag 0 (date/time) stays a string.
|
||||
|
||||
??? example "Example: deserialize a JSON value from CBOR"
|
||||
|
||||
|
||||
@@ -301,8 +301,6 @@ representation using the length of the byte array.
|
||||
[`json::from_cbor`](../api/basic_json/from_cbor.md) (see
|
||||
[`cbor_tag_handler_t`](../api/basic_json/cbor_tag_handler_t.md)).
|
||||
|
||||
With `ignore`, the subtype is dropped (`"subtype": null`); with `store`, the tag is kept (`"subtype": 42`):
|
||||
|
||||
```json
|
||||
{
|
||||
"binary": {
|
||||
@@ -312,15 +310,6 @@ representation using the length of the byte array.
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"binary": {
|
||||
"bytes": [202, 254, 186, 190],
|
||||
"subtype": 42
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### MessagePack
|
||||
|
||||
[MessagePack](binary_formats/messagepack.md) supports binary values and subtypes. If a subtype is given, the ext family
|
||||
|
||||
@@ -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()"]
|
||||
```
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@ 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>
|
||||
|
||||
### Iteration order for objects
|
||||
|
||||
@@ -112,7 +114,9 @@ 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>
|
||||
|
||||
??? example "Example: reverse iteration with `rbegin()` and `rend()`"
|
||||
|
||||
|
||||
@@ -27,7 +27,9 @@ 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>
|
||||
|
||||
### Number interoperability
|
||||
|
||||
@@ -69,9 +71,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
|
||||
|
||||
@@ -368,7 +368,8 @@ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
|
||||
`#!cpp std::from_chars` is not available or declines the token, and `std::strtold` reads up to the null character. A type whose `data()`
|
||||
is not null-terminated does not fail to compile; with such a `NumberFloatType` it can silently misparse
|
||||
floating-point numbers.
|
||||
- `append(const char*, size_type)`, used by [`dump`](../../api/basic_json/dump.md) and by the binary readers. The library's internal string concatenation additionally has
|
||||
- `append(const char*, size_type)`, used by [`dump`](../../api/basic_json/dump.md), and `append(const StringType&)`,
|
||||
used by the CBOR reader for indefinite-length strings. The library's internal string concatenation additionally has
|
||||
to append a `#!cpp char` and a `#!cpp const char*`; for each it selects between `append(arg)`, `#!cpp operator+=`,
|
||||
`append(first, last)`, and `append(data, size)`.
|
||||
- The comparison operator `==` against another `StringType`, and `<` for use as a key of the chosen
|
||||
@@ -677,7 +678,8 @@ using binary_t = nlohmann::byte_container_with_subtype<BinaryType>;
|
||||
[`StringType`](#stringtype), whose non-`#!cpp const` `data()` is C++17 and later only.
|
||||
- Default-constructible, copy-constructible, and move-constructible.
|
||||
- Member functions `size()`, `empty()`, `data()`, `resize()`, `operator[]`, `back()`, `begin()`, `end()`, `cbegin()`,
|
||||
and `cend()` with random-access iterators. `push_back()` is **not** required.
|
||||
and `cend()` with random-access iterators, and `insert(pos, first, last)`, which the CBOR reader uses to join the
|
||||
chunks of an indefinite-length byte string. `push_back()` is **not** required.
|
||||
- Comparison operators: `==` is used by
|
||||
[`byte_container_with_subtype`](../../api/byte_container_with_subtype/index.md), the relational operators by
|
||||
[`basic_json`'s comparison operators](../../api/basic_json/operator_le.md).
|
||||
|
||||
|
Before Width: | Height: | Size: 45 KiB |
@@ -0,0 +1,39 @@
|
||||
<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​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.</title>
|
||||
<text class="fg-h" x="16" y="28">J​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">"name"</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">: "Berlin"</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">"location"</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>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
@@ -0,0 +1 @@
|
||||
<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
|
||||
|
After Width: | Height: | Size: 395 KiB |
@@ -0,0 +1 @@
|
||||
<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
|
||||
|
After Width: | Height: | Size: 394 KiB |
|
Before Width: | Height: | Size: 36 KiB |
@@ -0,0 +1,66 @@
|
||||
<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​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">−</text>
|
||||
<text class="rr-c" x="257" y="49">0</text>
|
||||
<text class="rr-b" x="201" y="97">digit 1–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">−</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>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
@@ -1,447 +1,28 @@
|
||||
<?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 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)"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1 @@
|
||||
<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
|
||||
|
After Width: | Height: | Size: 5.8 KiB |
@@ -1,63 +1,11 @@
|
||||
---
|
||||
template: home.html
|
||||
hide:
|
||||
- toc
|
||||
---
|
||||
|
||||
# JSON for Modern C++
|
||||
|
||||

|
||||
|
||||
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__
|
||||
@@ -112,11 +60,5 @@ Conda, and more), and pkg-config.
|
||||
|
||||
</div>
|
||||
|
||||
!!! 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).
|
||||

|
||||

|
||||
@@ -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"]
|
||||
```
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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); }
|
||||
});
|
||||
})();
|
||||
@@ -1,5 +1,6 @@
|
||||
# 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/
|
||||
|
||||
@@ -14,33 +15,38 @@ copyright: Copyright © 2013-2026 Niels Lohmann
|
||||
# Configuration
|
||||
theme:
|
||||
name: material
|
||||
custom_dir: overrides
|
||||
language: en
|
||||
palette:
|
||||
- media: '(prefers-color-scheme: light)'
|
||||
scheme: default
|
||||
primary: indigo
|
||||
accent: indigo
|
||||
primary: custom
|
||||
accent: custom
|
||||
toggle:
|
||||
icon: material/brightness-7
|
||||
name: Switch to dark mode
|
||||
- media: '(prefers-color-scheme: dark)'
|
||||
scheme: slate
|
||||
primary: indigo
|
||||
accent: indigo
|
||||
primary: custom
|
||||
accent: custom
|
||||
toggle:
|
||||
icon: material/brightness-4
|
||||
name: Switch to light mode
|
||||
icon:
|
||||
repo: fontawesome/brands/github
|
||||
font:
|
||||
text: Roboto
|
||||
code: JetBrains Mono
|
||||
font: false
|
||||
logo: images/wordmark-header.svg
|
||||
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
|
||||
@@ -619,3 +625,7 @@ plugins:
|
||||
|
||||
extra_css:
|
||||
- css/custom.css
|
||||
- css/brand.css
|
||||
|
||||
extra_javascript:
|
||||
- js/mermaid-brand.js
|
||||
@@ -0,0 +1,15 @@
|
||||
{#- 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 %}
|
||||
@@ -0,0 +1,33 @@
|
||||
{#- 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"><iostream></span>
|
||||
<span class="nj-k">#include</span> <span class="nj-s"><nlohmann/json.hpp></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 << j.dump(<span class="nj-n">4</span>) << <span class="nj-s">"\n"</span>;
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,103 @@
|
||||
"""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​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&#8203;SON', 'J​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​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​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()
|
||||
@@ -0,0 +1,76 @@
|
||||
"""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​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, "−"); 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–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, "−")
|
||||
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​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")
|
||||
@@ -0,0 +1 @@
|
||||
fonts/
|
||||
@@ -0,0 +1,73 @@
|
||||
# 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)
|
||||
@@ -0,0 +1,27 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,461 @@
|
||||
#!/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))
|
||||
@@ -0,0 +1,412 @@
|
||||
<!--
|
||||
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';
|
||||
}
|
||||
```
|
||||
@@ -1218,10 +1218,6 @@ class binary_reader
|
||||
// level as required by RFC 8949, Section 3.2.3
|
||||
bool indefinite = false;
|
||||
|
||||
// the chunk currently being validated; only used for the chunks of an
|
||||
// indefinite-length string when the UTF-8 check can do something
|
||||
string_t chunk;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("string")))
|
||||
@@ -1241,82 +1237,26 @@ class binary_reader
|
||||
}
|
||||
|
||||
// a break marker closes the indefinite-length string; outside
|
||||
// of one it falls through to the error below. Every chunk has
|
||||
// been checked on its own, so there is nothing left to check.
|
||||
// of one it falls through to the error below
|
||||
if (indefinite && current == 0xFF)
|
||||
{
|
||||
return true;
|
||||
return check_string_utf8(result, context);
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_string_chunk(result, indefinite)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!indefinite)
|
||||
{
|
||||
return get_cbor_string_chunk(result, false) && check_string_utf8(result, context);
|
||||
}
|
||||
|
||||
if (error_handler == error_handler_t::keep)
|
||||
{
|
||||
// nothing to check: read the chunk directly into the result
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_string_chunk(result, true)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// RFC 8949, Section 3.2.3: each chunk is a text string of its
|
||||
// own and must be valid UTF-8 by itself, i.e., a code point
|
||||
// cannot be spread over two chunks
|
||||
chunk.clear();
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_string_chunk(chunk, true) || !check_string_utf8(chunk, context)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
result.append(chunk.data(), chunk.size());
|
||||
return check_string_utf8(result, context);
|
||||
}
|
||||
|
||||
get();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief skips the tags in front of a CBOR object key
|
||||
|
||||
A tag may wrap any data item, so it may also wrap a key. Like in front of a
|
||||
value, the tags are skipped for the handlers ignore and store; with the
|
||||
handler error they are left to @ref get_cbor_object_key, which rejects the
|
||||
key. The tag loop is iterative, so the number of tags is not limited by the
|
||||
recursion depth.
|
||||
|
||||
@param[in] tag_handler how CBOR tags should be treated
|
||||
|
||||
@return whether the tags could be skipped (false on EOF in a tag argument)
|
||||
|
||||
@pre @a current is the first byte of the key
|
||||
*/
|
||||
bool skip_cbor_key_tags(const cbor_tag_handler_t tag_handler)
|
||||
{
|
||||
if (tag_handler == cbor_tag_handler_t::error)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 0xC0..0xDB: tag with the tag number in the head (0xC0..0xD7) or in
|
||||
// the 1, 2, 4, or 8 bytes that follow (0xD8..0xDB)
|
||||
while (current >= 0xC0 && current <= 0xDB)
|
||||
{
|
||||
if (current >= 0xD8)
|
||||
{
|
||||
std::uint64_t tag_to_ignore{};
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_argument(tag_to_ignore)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
get();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reads a CBOR object key
|
||||
|
||||
@@ -1683,10 +1623,6 @@ class binary_reader
|
||||
|
||||
if (top.is_object)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!skip_cbor_key_tags(tag_handler)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (JSON_HEDLEY_UNLIKELY(!read_object_key<&binary_reader::get_cbor_object_key>(key)))
|
||||
{
|
||||
return false;
|
||||
@@ -4194,9 +4130,9 @@ class binary_reader
|
||||
// sequence), UBJSON, BJData, or BSON requires a decoder to reject
|
||||
// ill-formed UTF-8. Checking (and, with @ref error_handler_t::strict,
|
||||
// rejecting, or with `replace`/`ignore`, sanitizing) is opt-in via
|
||||
// @ref error_handler, applied by @ref check_string_utf8 at the call
|
||||
// site to the whole string or, for an indefinite-length CBOR string,
|
||||
// to each chunk on its own (RFC 8949, Section 3.2.3).
|
||||
// @ref error_handler, applied once the whole string (all chunks of
|
||||
// an indefinite-length CBOR string included) has been assembled, by
|
||||
// @ref check_string_utf8 at the call site.
|
||||
return get_bytes(len, "string", result);
|
||||
}
|
||||
|
||||
|
||||
@@ -18685,10 +18685,6 @@ class binary_reader
|
||||
// level as required by RFC 8949, Section 3.2.3
|
||||
bool indefinite = false;
|
||||
|
||||
// the chunk currently being validated; only used for the chunks of an
|
||||
// indefinite-length string when the UTF-8 check can do something
|
||||
string_t chunk;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("string")))
|
||||
@@ -18708,82 +18704,26 @@ class binary_reader
|
||||
}
|
||||
|
||||
// a break marker closes the indefinite-length string; outside
|
||||
// of one it falls through to the error below. Every chunk has
|
||||
// been checked on its own, so there is nothing left to check.
|
||||
// of one it falls through to the error below
|
||||
if (indefinite && current == 0xFF)
|
||||
{
|
||||
return true;
|
||||
return check_string_utf8(result, context);
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_string_chunk(result, indefinite)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!indefinite)
|
||||
{
|
||||
return get_cbor_string_chunk(result, false) && check_string_utf8(result, context);
|
||||
}
|
||||
|
||||
if (error_handler == error_handler_t::keep)
|
||||
{
|
||||
// nothing to check: read the chunk directly into the result
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_string_chunk(result, true)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// RFC 8949, Section 3.2.3: each chunk is a text string of its
|
||||
// own and must be valid UTF-8 by itself, i.e., a code point
|
||||
// cannot be spread over two chunks
|
||||
chunk.clear();
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_string_chunk(chunk, true) || !check_string_utf8(chunk, context)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
result.append(chunk.data(), chunk.size());
|
||||
return check_string_utf8(result, context);
|
||||
}
|
||||
|
||||
get();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief skips the tags in front of a CBOR object key
|
||||
|
||||
A tag may wrap any data item, so it may also wrap a key. Like in front of a
|
||||
value, the tags are skipped for the handlers ignore and store; with the
|
||||
handler error they are left to @ref get_cbor_object_key, which rejects the
|
||||
key. The tag loop is iterative, so the number of tags is not limited by the
|
||||
recursion depth.
|
||||
|
||||
@param[in] tag_handler how CBOR tags should be treated
|
||||
|
||||
@return whether the tags could be skipped (false on EOF in a tag argument)
|
||||
|
||||
@pre @a current is the first byte of the key
|
||||
*/
|
||||
bool skip_cbor_key_tags(const cbor_tag_handler_t tag_handler)
|
||||
{
|
||||
if (tag_handler == cbor_tag_handler_t::error)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 0xC0..0xDB: tag with the tag number in the head (0xC0..0xD7) or in
|
||||
// the 1, 2, 4, or 8 bytes that follow (0xD8..0xDB)
|
||||
while (current >= 0xC0 && current <= 0xDB)
|
||||
{
|
||||
if (current >= 0xD8)
|
||||
{
|
||||
std::uint64_t tag_to_ignore{};
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_argument(tag_to_ignore)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
get();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reads a CBOR object key
|
||||
|
||||
@@ -19150,10 +19090,6 @@ class binary_reader
|
||||
|
||||
if (top.is_object)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!skip_cbor_key_tags(tag_handler)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (JSON_HEDLEY_UNLIKELY(!read_object_key<&binary_reader::get_cbor_object_key>(key)))
|
||||
{
|
||||
return false;
|
||||
@@ -21661,9 +21597,9 @@ class binary_reader
|
||||
// sequence), UBJSON, BJData, or BSON requires a decoder to reject
|
||||
// ill-formed UTF-8. Checking (and, with @ref error_handler_t::strict,
|
||||
// rejecting, or with `replace`/`ignore`, sanitizing) is opt-in via
|
||||
// @ref error_handler, applied by @ref check_string_utf8 at the call
|
||||
// site to the whole string or, for an indefinite-length CBOR string,
|
||||
// to each chunk on its own (RFC 8949, Section 3.2.3).
|
||||
// @ref error_handler, applied once the whole string (all chunks of
|
||||
// an indefinite-length CBOR string included) has been assembled, by
|
||||
// @ref check_string_utf8 at the call site.
|
||||
return get_bytes(len, "string", result);
|
||||
}
|
||||
|
||||
|
||||
@@ -1807,93 +1807,6 @@ TEST_CASE("CBOR")
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0xa1, 0xff, 0x01}), true, false).is_discarded());
|
||||
}
|
||||
|
||||
SECTION("tag in front of a key")
|
||||
{
|
||||
const auto ignore = json::cbor_tag_handler_t::ignore;
|
||||
const auto store = json::cbor_tag_handler_t::store;
|
||||
const json expected = {{"a", 10}};
|
||||
|
||||
// the tags are skipped in front of a text string key, like in
|
||||
// front of a value
|
||||
const std::vector<std::vector<std::uint8_t>> tagged =
|
||||
{
|
||||
{0xA1, 0xC0, 0x61, 0x61, 0x0A}, // tag in the head
|
||||
{0xA1, 0xD8, 0x05, 0x61, 0x61, 0x0A}, // 1-byte tag number
|
||||
{0xA1, 0xD9, 0x01, 0x2C, 0x61, 0x61, 0x0A}, // 2-byte tag number
|
||||
{0xA1, 0xDA, 0x00, 0x01, 0x00, 0x00, 0x61, 0x61, 0x0A}, // 4-byte tag number
|
||||
{0xA1, 0xDB, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x61, 0x61, 0x0A}, // 8-byte tag number
|
||||
{0xA1, 0xC0, 0xD8, 0x05, 0xC1, 0x61, 0x61, 0x0A}, // chained tags
|
||||
{0xBF, 0xC0, 0x61, 0x61, 0x0A, 0xFF}, // indefinite-length map
|
||||
{0xBF, 0x61, 0x62, 0x01, 0xC0, 0x61, 0x61, 0x0A, 0xFF}, // second key tagged
|
||||
{0xA1, 0xC0, 0x7F, 0x61, 0x61, 0xFF, 0x0A}, // indefinite-length text key
|
||||
{0xA1, 0xC0, 0x61, 0x61, 0xC0, 0x0A}, // tagged key and value
|
||||
};
|
||||
|
||||
for (const auto& input : tagged)
|
||||
{
|
||||
CAPTURE(input)
|
||||
json const j_ignore = json::from_cbor(input, true, true, ignore);
|
||||
json const j_store = json::from_cbor(input, true, true, store);
|
||||
CHECK(j_ignore.at("a") == 10);
|
||||
CHECK(j_store.at("a") == 10);
|
||||
CHECK(j_ignore == j_store);
|
||||
}
|
||||
CHECK(json::from_cbor(tagged[0], true, true, ignore) == expected);
|
||||
CHECK(json::from_cbor(tagged[1], true, true, store) == expected);
|
||||
|
||||
// nested maps
|
||||
CHECK(json::from_cbor(std::vector<std::uint8_t>({0xA1, 0xC0, 0x61, 0x61, 0xA1, 0xD8, 0x05, 0x61, 0x62, 0x01}), true, true, ignore) == json{{"a", {{"b", 1}}}});
|
||||
|
||||
// the tag handler error still rejects every tag
|
||||
for (const auto& input : tagged)
|
||||
{
|
||||
CAPTURE(input)
|
||||
json _;
|
||||
CHECK_THROWS_AS(_ = json::from_cbor(input), json::parse_error&);
|
||||
}
|
||||
{
|
||||
json _;
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<std::uint8_t>({0xA1, 0xD8, 0x05, 0x61, 0x61, 0x0A})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR object key: only string keys are supported, but found a tag; last byte: 0xD8", json::parse_error&);
|
||||
}
|
||||
|
||||
// a tag does not turn a key of another type into a text string
|
||||
{
|
||||
json _;
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<std::uint8_t>({0xA1, 0xC0, 0x01, 0x01}), true, true, ignore), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing CBOR object key: only string keys are supported, but found an unsigned integer; last byte: 0x01", json::parse_error&);
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<std::uint8_t>({0xA1, 0xD8, 0x05, 0x41, 0x61, 0x01}), true, true, store), "[json.exception.parse_error.113] parse error at byte 4: syntax error while parsing CBOR object key: only string keys are supported, but found a byte string; last byte: 0x41", json::parse_error&);
|
||||
CHECK(json::from_cbor(std::vector<std::uint8_t>({0xA1, 0xC0, 0x01, 0x01}), true, false, ignore).is_discarded());
|
||||
}
|
||||
|
||||
// end of input after the tag, or inside the tag number
|
||||
{
|
||||
json _;
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<std::uint8_t>({0xA1, 0xC0}), true, true, ignore), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&);
|
||||
CHECK_THROWS_AS(_ = json::from_cbor(std::vector<std::uint8_t>({0xA1, 0xD8}), true, true, ignore), json::parse_error&);
|
||||
CHECK_THROWS_AS(_ = json::from_cbor(std::vector<std::uint8_t>({0xA1, 0xDB, 0x00, 0x00}), true, true, store), json::parse_error&);
|
||||
CHECK_THROWS_AS(_ = json::from_cbor(std::vector<std::uint8_t>({0xBF, 0xC0}), true, true, store), json::parse_error&);
|
||||
}
|
||||
|
||||
// SAX: the key event is reported without the tag (start_object,
|
||||
// key, number_unsigned, end_object are four events)
|
||||
{
|
||||
const std::vector<std::uint8_t> input = {0xA1, 0xC0, 0x61, 0x61, 0x0A};
|
||||
SaxCountdown four(4);
|
||||
CHECK(json::sax_parse(input, &four, json::input_format_t::cbor, true, false, false, ignore));
|
||||
SaxCountdown three(3);
|
||||
CHECK(!json::sax_parse(input, &three, json::input_format_t::cbor, true, false, false, ignore));
|
||||
SaxCountdown error_handler(4);
|
||||
CHECK(!json::sax_parse(input, &error_handler, json::input_format_t::cbor));
|
||||
}
|
||||
|
||||
// a long chain of tags needs no recursion
|
||||
{
|
||||
std::vector<std::uint8_t> input = {0xA1};
|
||||
input.insert(input.end(), 100000, 0xC0);
|
||||
input.insert(input.end(), {0x61, 0x61, 0x0A});
|
||||
CHECK(json::from_cbor(input, true, true, ignore) == expected);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("non-string key (see #2766 and #3381)")
|
||||
{
|
||||
// only text strings map to JSON object keys; any other key is
|
||||
@@ -2002,10 +1915,9 @@ TEST_CASE("CBOR")
|
||||
{
|
||||
json _;
|
||||
|
||||
// with the default error handler keep, the chunks are concatenated
|
||||
// as is, without checking that each chunk is valid UTF-8 on its
|
||||
// own (RFC 8949, Section 3.2.3), so a code point split across two
|
||||
// chunks yields a valid string
|
||||
// the chunks are concatenated as is, without checking that each
|
||||
// chunk is valid UTF-8 on its own (RFC 8949, Section 3.2.3), so
|
||||
// a code point split across two chunks yields a valid string
|
||||
CHECK_NOTHROW(_ = json::from_cbor(std::vector<uint8_t>({0x7f, 0x61, 0xc3, 0x61, 0xa9, 0xff})));
|
||||
CHECK(_ == "\xc3\xa9");
|
||||
CHECK(_.dump() == "\"\xc3\xa9\"");
|
||||
@@ -2025,63 +1937,6 @@ TEST_CASE("CBOR")
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x7f, 0x62, 0xc3, 0xa9, 0x62, 0xc3, 0xb6, 0xff})) == "\xc3\xa9\xc3\xb6");
|
||||
}
|
||||
|
||||
SECTION("ill-formed UTF-8 in a chunk of an indefinite-length string (RFC 8949, Section 3.2.3)")
|
||||
{
|
||||
const auto strict = json::error_handler_t::strict;
|
||||
const auto replace = json::error_handler_t::replace;
|
||||
const auto ignore = json::error_handler_t::ignore;
|
||||
const auto keep = json::error_handler_t::keep;
|
||||
const auto tags = json::cbor_tag_handler_t::error;
|
||||
|
||||
// a code point split over two chunks
|
||||
const std::vector<std::uint8_t> split = {0x7f, 0x61, 0xc3, 0x61, 0xa9, 0xff};
|
||||
// a 4-byte code point split over two chunks
|
||||
const std::vector<std::uint8_t> split_emoji = {0x7f, 0x62, 0xf0, 0x9f, 0x62, 0x98, 0x80, 0xff};
|
||||
// the same in an object key
|
||||
const std::vector<std::uint8_t> split_key = {0xa1, 0x7f, 0x61, 0xc3, 0x61, 0xa9, 0xff, 0x01};
|
||||
// a valid chunk followed by one with a truncated code point
|
||||
const std::vector<std::uint8_t> truncated = {0x7f, 0x62, 0x41, 0xc3, 0x61, 0xa9, 0xff};
|
||||
|
||||
// strict: the string is invalid
|
||||
for (const auto& input :
|
||||
{
|
||||
split, split_emoji, split_key, truncated
|
||||
})
|
||||
{
|
||||
CAPTURE(input)
|
||||
json _;
|
||||
CHECK_THROWS_AS(_ = json::from_cbor(input, true, true, tags, strict), json::parse_error&);
|
||||
CHECK(json::from_cbor(input, true, false, tags, strict).is_discarded());
|
||||
}
|
||||
{
|
||||
json _;
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(split, true, true, tags, strict), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing CBOR string: invalid string: ill-formed UTF-8 byte", json::parse_error&);
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(split_key, true, true, tags, strict), "[json.exception.parse_error.113] parse error at byte 4: syntax error while parsing CBOR key: invalid string: ill-formed UTF-8 byte", json::parse_error&);
|
||||
}
|
||||
|
||||
// replace and ignore: every chunk is sanitized on its own
|
||||
CHECK(json::from_cbor(split, true, true, tags, replace) == "\xEF\xBF\xBD\xEF\xBF\xBD");
|
||||
CHECK(json::from_cbor(split, true, true, tags, ignore) == "");
|
||||
CHECK(json::from_cbor(truncated, true, true, tags, replace) == "A\xEF\xBF\xBD\xEF\xBF\xBD");
|
||||
CHECK(json::from_cbor(truncated, true, true, tags, ignore) == "A");
|
||||
CHECK(json::from_cbor(split_key, true, true, tags, replace) == json{{"\xEF\xBF\xBD\xEF\xBF\xBD", 1}});
|
||||
|
||||
// keep: unchanged, the chunks are concatenated as is
|
||||
CHECK(json::from_cbor(split, true, true, tags, keep) == "\xc3\xa9");
|
||||
CHECK(json::from_cbor(split_emoji, true, true, tags, keep) == "\xf0\x9f\x98\x80");
|
||||
|
||||
// conforming input is unaffected by the error handler
|
||||
const std::vector<std::uint8_t> valid = {0x7f, 0x62, 0xc3, 0xa9, 0x64, 0xf0, 0x9f, 0x98, 0x80, 0x60, 0xff};
|
||||
for (const auto handler :
|
||||
{
|
||||
strict, replace, ignore, keep
|
||||
})
|
||||
{
|
||||
CHECK(json::from_cbor(valid, true, true, tags, handler) == "\xc3\xa9\xf0\x9f\x98\x80");
|
||||
CHECK(json::from_cbor(std::vector<std::uint8_t>({0x7f, 0xff}), true, true, tags, handler) == "");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("many chunks in indefinite-length string")
|
||||
{
|
||||
constexpr std::size_t chunks = 100000;
|
||||
|
||||